C ++ ने जोर से टाइप किया


49

मैं संकलन चरण में कीड़े के एक निश्चित वर्ग को पकड़ने के लिए दृढ़ता से टाइप किए गए टाइफेड घोषित करने का एक तरीका सोचने की कोशिश कर रहा हूं। यह अक्सर ऐसा होता है कि मैं कई प्रकार के आईडी, या स्थिति या वेग के लिए एक सदिश टाइप करूँगा:

typedef int EntityID;
typedef int ModelID;
typedef Vector3 Position;
typedef Vector3 Velocity;

यह कोड की मंशा को और अधिक स्पष्ट कर सकता है, लेकिन कोडिंग की एक लंबी रात के बाद व्यक्ति विभिन्न प्रकार के आईडी की तुलना करने या शायद एक वेग में एक स्थिति जोड़ने जैसी मूर्खतापूर्ण गलतियां कर सकता है।

EntityID eID;
ModelID mID;

if ( eID == mID ) // <- Compiler sees nothing wrong
{ /*bug*/ }


Position p;
Velocity v;

Position newP = p + v; // bug, meant p + v*s but compiler sees nothing wrong

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

सबसे पहले, आप आधार प्रकार को एक टेम्पलेट के रूप में घोषित करते हैं। टेम्पलेट पैरामीटर का उपयोग परिभाषा में किसी भी चीज के लिए नहीं किया जाता है, हालांकि:

template < typename T >
class IDType
{
    unsigned int m_id;

    public:
        IDType( unsigned int const& i_id ): m_id {i_id} {};
        friend bool operator==<T>( IDType<T> const& i_lhs, IDType<T> const& i_rhs );
};

मित्र कार्यों को वास्तव में वर्ग परिभाषा से पहले घोषित किए जाने की आवश्यकता होती है, जिसे टेम्पलेट वर्ग की एक आगे की घोषणा की आवश्यकता होती है।

हम तब आधार प्रकार के लिए सभी सदस्यों को परिभाषित करते हैं, बस यह याद रखना कि यह एक टेम्पलेट वर्ग है।

अंत में, जब हम इसका उपयोग करना चाहते हैं, हम इसे टाइप करते हैं:

class EntityT;
typedef IDType<EntityT> EntityID;
class ModelT;
typedef IDType<ModelT> ModelID;

प्रकार अब पूरी तरह से अलग हैं। यदि आप उन्हें एक ModelID खिलाने की कोशिश करते हैं, तो EntityID लेने वाले कार्य एक संकलक त्रुटि को फेंक देंगे, उदाहरण के लिए। आधार के प्रकार को टेम्प्लेट के रूप में घोषित करने के अलावा, जो मुद्दे उलझते हैं, वह भी काफी कॉम्पैक्ट है।

मैं उम्मीद कर रहा था कि किसी को भी इस विचार के बारे में टिप्पणी या आलोचना करना चाहिए था?

उदाहरण के लिए पदों और वेगों के मामले में यह लिखते समय एक मुद्दा जो दिमाग में आया, वह यह होगा कि मैं पहले की तरह स्वतंत्र रूप से टाइप के बीच रूपांतरण नहीं कर सकता। जहां एक स्केलर द्वारा एक वेक्टर को गुणा करने से पहले एक और वेक्टर दिया जाएगा, इसलिए मैं कर सकता था:

typedef float Time;
typedef Vector3 Position;
typedef Vector3 Velocity;

Time t = 1.0f;
Position p = { 0.0f };
Velocity v = { 1.0f, 0.0f, 0.0f };

Position newP = p + v*t;

अपने दृढ़ता से टाइप किए गए टाइपफ़ीड के साथ, मुझे संकलक को बताना होगा कि किसी स्थिति में समय के परिणाम से वेग को गुणा करना।

class TimeT;
typedef Float<TimeT> Time;
class PositionT;
typedef Vector3<PositionT> Position;
class VelocityT;
typedef Vector3<VelocityT> Velocity;

Time t = 1.0f;
Position p = { 0.0f };
Velocity v = { 1.0f, 0.0f, 0.0f };

Position newP = p + v*t; // Compiler error

इसे हल करने के लिए, मुझे लगता है कि मुझे हर रूपांतरण को स्पष्ट रूप से करना होगा, जो एक तरह का परेशान कर सकता है। दूसरी ओर, यह सीमा अन्य प्रकार की त्रुटियों को रोकने में मदद कर सकती है (जैसे, एक दूरी से वेग को गुणा करना, शायद, जो इस डोमेन में कोई मतलब नहीं होगा)। इसलिए मैं फटा हुआ हूं, और सोच रहा हूं कि क्या लोगों को मेरे मूल मुद्दे पर कोई राय है, या इसे हल करने के लिए मेरा दृष्टिकोण।


इस पर एक नज़र: zumalifeguard.wikia.com/wiki/Idtypes.idl
zumalifeguard

यहाँ एक ही सवाल है: stackoverflow.com/q/23726038/476681
B isовић

जवाबों:


39

ये प्रेत प्रकार के पैरामीटर हैं , अर्थात् , एक पैरामीटर प्रकार के पैरामीटर जो उनके प्रतिनिधित्व के लिए उपयोग नहीं किए जाते हैं, लेकिन एक ही प्रतिनिधित्व के साथ विभिन्न प्रकार के "रिक्त स्थान" को अलग करने के लिए।

और रिक्त स्थान की बात करते हुए, यह प्रेत प्रकारों का एक उपयोगी अनुप्रयोग है:

template<typename Space>
struct Point { double x, y; };

struct WorldSpace;
struct ScreenSpace;

// Conversions between coordinate spaces are explicit.
Point<ScreenSpace> project(Point<WorldSpace> p, const Camera& c) {  }

जैसा कि आपने देखा है, हालांकि, यूनिट प्रकारों के साथ कुछ कठिनाइयाँ हैं। एक चीज़ जो आप कर सकते हैं वह है मूलभूत घटकों पर पूर्णांक घातांक के एक वेक्टर में इकाइयों को विघटित करना:

template<typename T, int Meters, int Seconds>
struct Unit {
  Unit(const T& value) : value(value) {}
  T value;
};

template<typename T, int MA, int MB, int SA, int SB>
Unit<T, MA - MB, SA - SB>
operator/(const Unit<T, MA, SA>& a, const Unit<T, MB, SB>& b) {
  return a.value / b.value;
}

Unit<double, 0, 0> one(1);
Unit<double, 1, 0> one_meter(1);
Unit<double, 0, 1> one_second(1);

// Unit<double, 1, -1>
auto one_meter_per_second = one_meter / one_second;

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


2
हम्म, संचालन पर इकाइयों को लागू करने के लिए टेम्पलेट सिस्टम का उपयोग करना अच्छा है। इसके बारे में सोचा नहीं था, धन्यवाद! अब मैं सोच रहा हूं कि क्या आप मीटर और किलोमीटर के बीच रूपांतरण जैसी चीजों को लागू कर सकते हैं, उदाहरण के लिए।
कियान

@ कियान: संभवतः आप SI बेस इकाइयों का आंतरिक रूप से उपयोग करेंगे- m, kg, s, A, और c.- और सुविधा के लिए केवल एक उपनाम 1 किमी = 1000 मीटर परिभाषित करें।
जॉन पूर्डी

7

मेरे पास एक समान मामला था जहां मैं कुछ पूर्णांक मानों के विभिन्न अर्थों को अलग करना चाहता था, और उनके बीच निहित रूपांतरणों को मना करता था। मैंने इस तरह एक सामान्य वर्ग लिखा:

template <typename T, typename Meaning>
struct Explicit
{
  //! Default constructor does not initialize the value.
  Explicit()
  { }

  //! Construction from a fundamental value.
  Explicit(T value)
    : value(value)
  { }

  //! Implicit conversion back to the fundamental data type.
  inline operator T () const { return value; }

  //! The actual fundamental value.
  T value;
};

बेशक अगर आप और भी सुरक्षित होना चाहते हैं, तो आप Tकंस्ट्रक्टर explicitको भी अपना बना सकते हैं । Meaningतो इस तरह से प्रयोग किया जाता है:

typedef Explicit<int, struct EntityIDTag> EntityID;
typedef Explicit<int, struct ModelIDTag> ModelID;

1
यह दिलचस्प है, लेकिन मुझे यकीन नहीं है कि यह काफी मजबूत है। यह सुनिश्चित करेगा कि यदि मैं टाइप किए गए प्रकार के साथ एक फ़ंक्शन की घोषणा करता हूं, तो केवल सही तत्वों को पैरामीटर के रूप में उपयोग किया जा सकता है, जो अच्छा है। लेकिन हर दूसरे उपयोग के लिए यह मापदंडों के मिश्रण को रोकने के बिना सिंटैक्टिक ओवरहेड जोड़ता है। ऑपरेशन जैसे कि तुलना करना। ऑपरेटर == (इंट, इंट) बिना किसी शिकायत के एक एंटिटी और एक मॉडलआईडी लेगा (भले ही स्पष्ट रूप से कहा जाए कि मैंने इसे डाला है, यह मुझे गलत चर का उपयोग करने से नहीं रोकता है)।
कियान

हाँ। मेरे मामले में मुझे एक दूसरे को विभिन्न प्रकार के आईडी असाइन करने से रोकना था। तुलना और अंकगणितीय संचालन मेरी मुख्य चिंता नहीं थी। उपरोक्त निर्माण असाइनमेंट पर प्रतिबंध लगाएगा, लेकिन अन्य संचालन नहीं।
मनसूबे

यदि आप इसमें अधिक ऊर्जा लगाने के लिए तैयार हैं, तो आप एक बहुत ही (सामान्य) जेनेरिक संस्करण बना सकते हैं, जो ऑपरेटरों को भी संभालता है, स्पष्ट वर्ग को सबसे आम ऑपरेटरों को लपेटकर। एक उदाहरण के लिए pastebin.com/FQDuAXdu देखें - आपको यह निर्धारित करने के लिए कुछ काफी जटिल SFINAE निर्माण की आवश्यकता है कि क्या आवरण वर्ग वास्तव में लिपटे ऑपरेटरों को प्रदान करता है या नहीं ( इस SO प्रश्न को देखें )। ध्यान रहे, यह अभी भी सभी मामलों को कवर नहीं कर सकता है और परेशानी के लायक नहीं हो सकता है।
मनसूबे

जबकि वाक्यविन्यास सुरुचिपूर्ण है, यह समाधान पूर्णांक-प्रकारों के लिए महत्वपूर्ण प्रदर्शन जुर्माना लगाएगा। पूर्णांक रजिस्टरों के माध्यम से पारित किया जा सकता है, संरचना (यहां तक ​​कि एक पूर्णांक युक्त) नहीं कर सकते।
घोस्टड्राइपर

1

मुझे यकीन नहीं है कि निम्नलिखित उत्पादन कोड में कैसे काम करता है (मैं एक C ++ / प्रोग्रामिंग शुरुआती, जैसे, CS101 शुरुआत) हूं, लेकिन मैंने इसे C ++ के मैक्रो सिस का उपयोग करके पकाया।

#define newtype(type_, type_alias) struct type_alias { \

/* make a new struct type with one value field
of a specified type (could be another struct with appropriate `=` operator*/

    type_ inner_public_field_thing; \  // the masked_value
    \
    explicit type_alias( type_ new_value ) { \  // the casting through a constructor
    // not sure how this'll work when casting non-const values
    // (like `type_alias(variable)` as opposed to `type_alias(bare_value)`
        inner_public_field_thing = new_value; } }

नोट: कृपया मुझे आपके द्वारा किए गए किसी भी नुकसान / सुधार के बारे में बताएं।
Noein

1
क्या आप कुछ कोड जोड़कर दिखा सकते हैं कि इस मैक्रो का उपयोग कैसे किया जाता है - जैसे मूल प्रश्न में उदाहरणों पर? यदि हां, तो यह एक महान जवाब है।
जे एलस्टन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.