c++ समस सी हेडर की घोषणाओं के साथ ग्लोबल नेमस्पेस को प्रदूषित करने के लिए कैसे नहीं?




दिल्ली में प्रदूषण की समस्या कारण और समाधान (3)

मैं सी ++ में सी लाइब्रेरी को एक आधुनिक, उच्च स्तरीय और मुहावरेदार सी ++ लाइब्रेरी बनाने की कोशिश कर रहा हूं। मैं क्या करना चाहता / चाहती हूं, सी ऑब्जेक्ट पूरी तरह से अपारदर्शी और / या सी + + कोड से सीधे अनुपलब्ध और उच्च-स्तरीय विकल्पों के साथ उन्हें लपेटो / प्रतिस्थापित करना है।

मैं जिस समस्या का सामना कर रहा हूं वह सरल है: मैं केवल सी हेडर को सी ++ स्रोत में शामिल करना चाहता हूं, ताकि सी + + हेडर को शामिल किया जाए जिसमें सी हेडर की घोषणाएं भी शामिल नहीं होंगी, अर्थात यह दूषित नहीं करेगा वैश्विक नामस्थान

लेकिन ऐसा लगता है कि शीर्ष लेख और स्रोत फ़ाइलों की सही पृथक्करण मुझे ऐसा करने की अनुमति नहीं देता है। यहां मेरी समस्या का एक बहुत ही गुंबददार संस्करण है, टिप्पणी आपको बाकी बताएगी:

my_header.h:

typedef enum
{
    my_Consts_ALPHA = /* some special value */,
    my_Consts_BETA  = /* other special value */,
} my_Consts;

typedef struct
{
    // members...
} my_Type;

void
my_Type_method(my_Type *const,
               my_Enum);

my_header.hpp:

namespace my
{
    enum class Consts; // <-- This header is missing the constant values of
                       //     this enum, because its values are defined by
                       //     the C header :( 

    class Type : public my_Type // <-- The super struct is coming from the
                                //     C header, but I don't want to include
                                //     that header here :(
    {
        public:
            void
            method(Consts constant);
    };
}

my_source.cpp:

extern "C"
{
    #include "my_header.h"
}

#include "my_header.hpp"

namespace my
{
    enum class Consts
    {
        ALPHA = my_Consts_ALPHA,
        BETA  = my_Consts_BETA,
    };

    void
    Type::method(Consts constant)
    {
        my_Type_method(static_cast<my_Type *const>(this),
                       static_cast<my_Consts>(constant));
    }
}

तो मेरे प्रश्न हैं: क्या मैं यहाँ कुछ बहुत स्पष्ट कर रहा हूं? क्या यह भी हासिल करना संभव है? क्या कोई ऐसी चाल है जो मुझे पता नहीं है?


मुझे यकीन नहीं है कि यह भाषा की कानूनी है, लेकिन मुझे लगता है कि extern "C" को केवल असम्बद्ध कार्यों में शामिल किया गया है, इसलिए जब तक आप उन्हें। सीपी फ़ाइल में रखते हैं, आप इस के साथ दूर हो सकते हैं।

यह थोड़ा अपव्यय है, लेकिन यह gcc 4.3.5 के साथ काम करता है। यह दर्शाता है कि आप सी फ़ंक्शंस का उपयोग कर सकते हैं, जबकि उन्हें एक नेमस्पेस में छिपाते हैं।

मैं struct_t inheritable साथ परेशान नहीं किया था, लेकिन यह शायद काम करना चाहिए मुझे नहीं पता है कि आप enum class को खींच सकते हैं।

foo.h

#ifndef foo_H
#define foo_H

typedef enum {
    ALPHA,
    BETA
} enum_t;

typedef struct
{
    int i;
} struct_t;

void printit(struct_t print_me);

#endif // foo_H

foo.c

#include <stdio.h>
#include "foo.h"

void printit (struct_t print_me)
{
    printf ("Hello World %d!\n", print_me.i);
}

bar.hpp

#ifndef bar_HPP
#define bar_HPP

namespace _foo {
    // Don't need extern "C" since we're not using functions
#include "foo.h"
}

struct based_on_struct_t // : public _foo:struct_t // Do you really have to derive?  It might be possible, but it's ugly
{
    _foo::struct_t i;
    double j;
    based_on_struct_t (int _i, double _j) : j(_j) { i.i = _i; }
    void print(void); // Gonna call printit, MUST be in .cpp
};

#endif // bar_HPP

bar.cpp

namespace _foo{
extern "C" {
#include "foo.h"
}
}

#include "bar.hpp"
#include <stdio.h>

void based_on_struct_t::print (void) {
    // Call the old version...
    printit(i);

    // And do new crap
    printf ("Goodbye World %d %f\n", i.i, j);
}

driver.cpp

#include "bar.hpp"

int main (void) {
    based_on_struct_t B(10, .1);

    B.print();

    return 0;
}

डेमो ...

$ gcc foo.c -c -O3
$ g++ foo.o bar.cpp driver.cpp
$ ./a.out
Hello World 10!
Goodbye World 10 0.100000
$

प्रश्न के जवाब में #AnalPhabet व्यंग्यात्मक सुझाव दिया है, कि एक #include namespace अंदर एक सी हेडर के अंदर का उपयोग करना चाहिए। @ एनएम ने पुष्टि की, यह वास्तव में एक काम करने वाला समाधान है, और अब मैंने इसे अपने स्वयं के सेटअप पर परीक्षण किया, और सौभाग्य से यह बहुत ठीक काम कर रहा है।

(हालांकि मुझे पता नहीं है, अगर यह कार्यान्वयन विशिष्ट है या नहीं, लेकिन मैंने दोनों g++ और clang++ पर परीक्षण किया और यह काम कर रहा है।)

यह अस्पष्टता समस्या का समाधान नहीं करता है, लेकिन कम से कम कच्चे सी डेटा तक सीधे पहुंचने में थोड़ा मुश्किल होता है क्योंकि यह अब एक अलग namespace रह रहा है, इसलिए उपयोगकर्ता आकस्मिक रूप से पहुंच नहीं सकते, लेकिन स्वेच्छा से।

इसलिए, my_header.hpp इस तरह दिखना चाहिए:

namespace my
{
    extern "C"
    {
        #include "my_header.h"
    }

    enum class Consts
    {
        ALPHA = my_Consts_ALPHA,
        BETA  = my_Consts_BETA,
    };

    class Type : public my_Type
    {
        public:
            void
            method(Consts constant);
    };
}

इसलिए जहां भी my_header.hpp #include , उपयोगकर्ता केवल निम्न सी मानों तक ही पहुंच सकता है:

my::my_Consts_ALPHA       // The wrapped value is => my::Consts::ALPHA
my::my_Type               // The wrapped value is => my::Type
my::my_Type_method(t,..)  // The wrapped value is => t.method(..)

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

स्वच्छ मुहावरेदार सी + + इंटरफ़ेस प्रदान करें, और कार्यान्वयन में केवल सी लाइब्रेरी का उपयोग करें।

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

जब तक मैं किसी प्रोफ़ाइलर लॉग में शीर्ष पर नहीं देखता हूं, मैं ऐसी रपट के कारण प्रदर्शन के बारे में चिंता नहीं करता। ज्यादातर मामलों में यह एक बाधा नहीं है

दोबारा, विभाजन इंटरफ़ेस और कार्यान्वयन आम तौर पर एक जीत है।

अद्यतन करें

प्रारंभ में, मैं सी लाइब्रेरी के आसपास सार्वभौमिक सी + + आवरण के बजाय परियोजना विशिष्ट C ++ इंटरफ़ेस के बारे में अधिक सोच रहा था।

बाहरी नाम extern "C" एक नामस्थान में लिपटे मुझे सही दिखता है (देखें § 7.5 का C ++ 11 मानक) लेकिन, मैंने इस तकनीक को जंगली में कभी नहीं देखा है

आप आगे बढ़ सकते हैं और नेस्टेड detail नाम स्थान को सी नामों के साथ my नाम स्थान को प्रदूषित करने के लिए नहीं जोड़ सकते। यह चाल हेडर केवल पुस्तकालयों में लोकप्रिय है:

namespace my
{
    namespace detail
    {
        extern "C"
        {
            #include "my_header.h"
        }
    }

    enum class Consts
    {
        ALPHA = detail::my_Consts_ALPHA,
        BETA  = detail::my_Consts_BETA,
    };

    class Type : public detail::my_Type
    {
        public:
            void
            method(Consts constant);
    };
}

ध्यान दें कि आप C फ़ंक्शंस पूरी तरह से अपारदर्शी नहीं बना सकते हैं या जब आप स्थैतिक पुस्तकालय से लिंक करते हैं तो उन्हें एक एकल नेमस्पेस में लपेटें। उनके बाहरी संबंध हैं और नामस्थान के बारे में कुछ नहीं पता है।

namespace A {
    extern "C" void my_Type_method(my_Type *const, my_Enum);
}

namespace B {
    extern "C" void my_Type_method(my_Type *const, my_Enum);
}

extern "C" void my_Type_method(my_Type *const, my_Enum);

मूल रूप से, इन सभी घोषणाएं एक ही सी फंक्शन को दर्शाती हैं। जैसे कि सी नामस्थान और ओवरलोडिंग का समर्थन नहीं करता, लिंकर आमतौर पर फ़ंक्शन के नाम को अनन्य पहचानकर्ता के रूप में उपयोग करते हैं (यहां तक ​​कि तर्क प्रकारों पर ध्यान नहीं दिया जाता है)।

वैसे भी, यह दृष्टिकोण सी अंतरफलक के लिए आकस्मिक पहुंच से बचने में मदद करेगा।





wrapper