कक्षाएं और ऑब्जेक्ट: मुझे वास्तव में उनका उपयोग करने के लिए कितने और कौन से फ़ाइल प्रकार की आवश्यकता है?


20

मुझे C ++ या C के साथ कोई पिछला अनुभव नहीं है, लेकिन पता है कि C # कैसे प्रोग्राम करें और Arduino सीख रहा हूं। मैं सिर्फ अपने रेखाचित्रों को व्यवस्थित करना चाहता हूं और अपनी सीमाओं के साथ भी Arduino भाषा के साथ काफी सहज हूं, लेकिन मैं वास्तव में अपने Arduino प्रोग्रामिंग के लिए एक वस्तु-उन्मुख दृष्टिकोण रखना चाहूंगा।

इसलिए मैंने देखा है कि कोड व्यवस्थित करने के लिए आपके पास निम्न तरीके (विस्तृत सूची नहीं) हो सकते हैं:

  1. एक एकल .ino फ़ाइल;
  2. एक ही फ़ोल्डर में एकाधिक .ino फाइलें (आईडीई क्या कहता है और "टैब" की तरह प्रदर्शित करता है);
  3. एक .hp और .cpp फ़ाइल के साथ एक ही फ़ोल्डर में .ino फ़ाइल;
  4. ऊपर के समान, लेकिन फाइलें Arduino प्रोग्राम फ़ोल्डर के अंदर एक स्थापित लाइब्रेरी हैं।

मैंने निम्नलिखित तरीकों के बारे में भी सुना है, लेकिन उन्हें अभी तक काम नहीं मिला है:

  • उसी, एकल .ino फ़ाइल में C ++ - शैली वर्ग की घोषणा (के बारे में सुना है, लेकिन कभी काम करते हुए नहीं देखा - यह भी संभव है?);
  • [पसंदीदा दृष्टिकोण] जिसमें एक .cpp फ़ाइल शामिल है जहां एक वर्ग घोषित किया गया है, लेकिन एक .h फ़ाइल का उपयोग किए बिना (इस दृष्टिकोण को पसंद करेंगे, क्या वह काम करना चाहिए?);

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


रुचि रखने वालों के लिए, हेडरलेस (केवल सीपीपी) वर्ग परिभाषाओं के बारे में एक दिलचस्प चर्चा है: प्रोग्रामर.स्टैकएक्सचेंज
com

जवाबों:


31

IDE चीजों को कैसे व्यवस्थित करता है

पहली बात, यह है कि IDE आपके "स्केच" को कैसे व्यवस्थित करता है:

  • मुख्य .ino। फ़ाइल फ़ोल्डर यह तो में है, के लिए के रूप में एक ही नाम से एक है foobar.inoमें foobarफ़ोल्डर - मुख्य फ़ाइल foobar.ino है।
  • .inoउस फ़ोल्डर में किसी भी अन्य फ़ाइलों को मुख्य फ़ाइल के अंत में (जहाँ भी मुख्य फ़ाइल है, वर्णानुक्रम में) वर्णमाला के क्रम में एक साथ सम्‍मिलित किया जाता है।
  • यह संक्षिप्त फ़ाइल एक .cppफ़ाइल (उदा। foobar.cpp) बन जाती है - इसे एक अस्थायी संकलन फ़ोल्डर में रखा जाता है।
  • प्रीप्रोसेसर "सहायक रूप से" फ़ंक्शन को उस फ़ाइल में मिलने वाले फ़ंक्शन के लिए प्रोटोटाइप बनाता है।
  • मुख्य फ़ाइल #include <libraryname>निर्देशों के लिए स्कैन की गई है । यह IDE को अस्थायी फ़ोल्डर में प्रत्येक (उल्लेखित) लाइब्रेरी से सभी प्रासंगिक फ़ाइलों को कॉपी करने के लिए ट्रिगर करता है, और उन्हें संकलित करने के लिए निर्देश उत्पन्न करता है।
  • स्केच फ़ोल्डर में कोई भी .c, .cppया .asmफ़ाइलों को अलग-अलग संकलन इकाइयों के रूप में बिल्ड प्रक्रिया में जोड़ा जाता है (अर्थात, वे अलग-अलग फ़ाइलों के रूप में सामान्य तरीके से संकलित किए जाते हैं)
  • किसी भी .hफाइल को अस्थायी संकलन फ़ोल्डर में भी कॉपी किया जाता है, इसलिए उन्हें आपकी .c या .cpp फाइलों द्वारा संदर्भित किया जा सकता है।
  • कंपाइलर निर्माण प्रक्रिया मानक फ़ाइलों में जोड़ता है (जैसे main.cpp)
  • निर्माण प्रक्रिया तब उपरोक्त सभी फाइलों को ऑब्जेक्ट फाइलों में संकलित करती है।
  • यदि संकलन चरण सफल होता है तो वे AVR मानक पुस्तकालयों (जैसे आपको दे रहे हैं strcpy) के साथ एक साथ जुड़े हुए हैं ।

इस सबका एक दुष्परिणाम यह है कि आप मुख्य स्केच (.ino फाइलें) को सभी अभिप्रायों और उद्देश्यों के लिए C ++ मान सकते हैं। यदि आप सावधान नहीं हैं तो फंक्शन प्रोटोटाइप पीढ़ी अस्पष्ट संदेश भेज सकती है।


प्री-प्रोसिजर क्वर्की से परहेज

इन idiosyncrasies से बचने का सबसे सरल तरीका है कि आप अपने मुख्य स्केच को खाली छोड़ दें (और किसी अन्य .inoफाइल का उपयोग न करें )। फिर दूसरा टैब (एक .cppफ़ाइल) बनाएं और उसमें अपना सामान इस तरह डालें:

#include <Arduino.h>

// put your sketch here ...

void setup ()
  {

  }  // end of setup

void loop ()
  {

  }  // end of loop

ध्यान दें कि आपको शामिल करने की आवश्यकता है Arduino.h। आईडीई मुख्य स्केच के लिए स्वचालित रूप से करता है, लेकिन अन्य संकलन इकाइयों के लिए, आपको यह करना होगा। अन्यथा यह स्ट्रिंग, हार्डवेयर रजिस्टरों आदि जैसी चीजों के बारे में नहीं जानता होगा।


सेटअप / मुख्य प्रतिमान से परहेज

आपको सेटअप / लूप अवधारणा के साथ नहीं चलना है। उदाहरण के लिए, आपकी .cpp फाइल हो सकती है:

#include <Arduino.h>

int main ()
  {
  init ();  // initialize timers
  Serial.begin (115200);
  Serial.println ("Hello, world");
  Serial.flush (); // let serial printing finish
  }  // end of main

बल पुस्तकालय समावेश

यदि आप "खाली स्केच" अवधारणा के साथ चलते हैं, तो आपको अभी भी परियोजना में उपयोग की जाने वाली पुस्तकालयों को शामिल करना होगा, उदाहरण के लिए आपकी मुख्य .inoफाइलों में:

#include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>

ऐसा इसलिए है क्योंकि IDE केवल लाइब्रेरी उपयोग के लिए मुख्य फ़ाइल को स्कैन करता है। प्रभावी रूप से आप मुख्य फ़ाइल को "प्रोजेक्ट" फ़ाइल के रूप में मान सकते हैं जो यह नामित करता है कि कौन से बाहरी पुस्तकालय उपयोग में हैं।


नामकरण के मुद्दे

  • अपने मुख्य स्केच को "main.cpp" नाम न दें - IDE में अपना स्वयं का main.cpp शामिल है, इसलिए यदि आप ऐसा करते हैं तो आपके पास एक डुप्लिकेट होगा।

  • अपनी .cpp फ़ाइल को अपने मुख्य .ino फ़ाइल के समान नाम न दें। चूंकि .ino फाइल प्रभावी रूप से .cpp फाइल बन जाती है, इससे आपको एक नाम भी मिल जाएगा।


उसी, एकल .ino फ़ाइल में C ++ - शैली वर्ग की घोषणा (के बारे में सुना है, लेकिन कभी काम करते हुए नहीं देखा - यह भी संभव है?);

हां, यह ठीक है:

class foo {
  public:
};

foo bar;

void setup () { }
void loop () { }

हालाँकि, आप सामान्य अभ्यास का पालन करने के लिए संभवतः सबसे अच्छा हैं: अपनी घोषणाओं को .hफाइलों में और अपनी परिभाषाओं (कार्यान्वयन) को .cpp(या .c) फाइलों में डालें ।

"शायद" क्यों?

जैसा कि मेरा उदाहरण दिखाता है कि आप एक फ़ाइल में सब कुछ एक साथ रख सकते हैं । बड़ी परियोजनाओं के लिए अधिक संगठित होना बेहतर है। आखिरकार आप एक मध्यम से बड़े आकार की परियोजना में मंच पर पहुंच जाते हैं, जहां आप चीजों को "ब्लैक बॉक्स" में अलग करना चाहते हैं - अर्थात, एक वर्ग जो एक काम करता है, वह अच्छी तरह से करता है, परीक्षण किया जाता है, और स्व-निहित है ( जहां तक ​​संभव हो)।

यदि यह वर्ग आपके प्रोजेक्ट में कई अन्य फ़ाइलों में उपयोग किया जाता है, तो यह वह जगह है जहाँ अलग .hऔर .cppफ़ाइलें चलन में आती हैं।

  • .hफ़ाइल वाणी वर्ग - यह है कि, यह पता करने के लिए यह क्या करता है अन्य फ़ाइलों के लिए पर्याप्त विस्तार, यह है क्या कार्य प्रदान करता है, और वे कैसे कहा जाता है।

  • .cppफ़ाइल परिभाषित करता है (औजार) वर्ग - यह है कि, यह वास्तव में काम करता है, और स्थिर वर्ग के सदस्यों को प्रदान करता है, उस वर्ग बनाने के अपने काम करते हैं। चूंकि आप केवल इसे एक बार लागू करना चाहते हैं, यह एक अलग फाइल में है।

  • .hफ़ाइल क्या अन्य फ़ाइलों में शामिल हो जाता है। .cppफ़ाइल वर्ग कार्यों को लागू करने आईडीई द्वारा एक बार संकलित किया गया है।

पुस्तकालय

यदि आप इस प्रतिमान का पालन करते हैं, तो आप पूरी कक्षा ( .hऔर .cppफाइलों) को एक पुस्तकालय में आसानी से स्थानांतरित करने के लिए तैयार हैं। फिर इसे कई परियोजनाओं के बीच साझा किया जा सकता है। सभी आवश्यक है कि एक फ़ोल्डर (जैसे। बनाने के लिए है myLibrary) और डाल .hऔर .cppइसे में फ़ाइलों (जैसे। myLibrary.hऔर myLibrary.cpp) और फिर अपने अंदर इस फ़ोल्डर में डाल दिया librariesहै, जहां आपके नमूने रखे गए हैं फ़ोल्डर (नोटबुक फ़ोल्डर) में फ़ोल्डर।

IDE को पुनरारंभ करें और यह अब इस लाइब्रेरी के बारे में जानता है। यह वास्तव में बहुत सरल है, और अब आप इस पुस्तकालय को कई परियोजनाओं पर साझा कर सकते हैं। मैं यह बहुत कुछ करता हूं।


थोड़ा और विस्तार यहाँ


अच्छा उत्तर। एक सबसे महत्वपूर्ण विषय, हालांकि, मेरे लिए अभी तक स्पष्ट नहीं हुआ है: क्यों हर कोई कहता है कि "आप शायद सामान्य अभ्यास का पालन करने के लिए सबसे अच्छे हैं : .h + .cpp"? यह बेहतर क्यों है? शायद हिस्सा क्यों ? और सबसे महत्वपूर्ण: मैं इसे कैसे नहीं कर सकता, अर्थात्, एक ही, एकल। Cpp फ़ाइल में इंटरफ़ेस और कार्यान्वयन (thas है, संपूर्ण वर्ग कोड) दोनों है? अभी के लिए आपका बहुत-बहुत धन्यवाद! : ओ)
हेल्टनबिकर

"शायद" आपके पास अलग-अलग फाइलें क्यों होनी चाहिए, इसका जवाब देने के लिए पैराग्राफ का एक और जोड़ा।
निक गैमन

1
आप इसे कैसे नहीं करते हैं? बस उन सभी को एक साथ मेरे उत्तर में चित्रित किया है, हालांकि आप पा सकते हैं कि प्रीप्रोसेसर आपके खिलाफ काम करता है। कुछ पूरी तरह से मान्य C ++ वर्ग की परिभाषाएँ विफल हो जाती हैं यदि उन्हें मुख्य .ino फ़ाइल में रखा जाता है।
निक गैमन

यदि आप अपनी .cpp फ़ाइलों में से दो में एक .H फ़ाइल शामिल करते हैं और वे .h फ़ाइल में कोड, जो कुछ की एक सामान्य आदत है, तो वे भी विफल हो जाएंगे। इसका खुला स्रोत, बस इसे स्वयं ठीक करें। यदि आप ऐसा करने में सहज नहीं हैं, तो आपको शायद खुले स्रोत का उपयोग नहीं करना चाहिए। सुंदर स्पष्टीकरण @ निक गैमन, मैंने अब तक जो कुछ भी देखा है उससे बेहतर है।

@ Spiked3 क्या यह चुनने का मामला नहीं है कि मैं किस चीज़ के साथ सबसे अधिक सहज हूं, अभी के लिए, यह जानने का विषय है कि मेरे लिए पहली जगह चुनने के लिए क्या उपलब्ध है। अगर मैं यह भी नहीं जानता कि मेरे विकल्प क्या हैं, तो मैं एक समझदार विकल्प कैसे बना सकता हूं, और प्रत्येक विकल्प ऐसा क्यों है? जैसा मैंने कहा, मुझे C ++ के साथ कोई पिछला अनुभव नहीं है, और ऐसा लगता है कि Arduino में C ++ को अतिरिक्त देखभाल की आवश्यकता हो सकती है, जैसा कि इस बहुत ही उत्तर में दिखाया गया है। लेकिन मुझे यकीन है कि आखिरकार मुझे इसकी समझ मिल रही है और पहिया को फिर से
लगाए

6

मेरी सलाह है कि चीजों को करने के विशिष्ट सी ++ तरीके से चिपके रहें: प्रत्येक वर्ग के लिए .hpp और .cpp फ़ाइलों में अलग-अलग इंटरफ़ेस और कार्यान्वयन।

कुछ कैच हैं:

  • आपको कम से कम एक .ino फ़ाइल की आवश्यकता है - मैं .cpp फ़ाइल के लिए एक सिमलिंक का उपयोग करता हूं, जहां मैं कक्षाओं को त्वरित करता हूं।
  • आपको कॉलबैक प्रदान करना होगा जिसे Arduino वातावरण उम्मीद करता है (सेतु, लूप, आदि)
  • कुछ मामलों में आप गैर-मानक अजीब चीजों से आश्चर्यचकित होंगे जो एक सामान्य से Arduino IDE को अलग करते हैं, जैसे कुछ पुस्तकालयों के स्वत: समावेश, लेकिन अन्य नहीं।

या, आप Arduino IDE को खोद सकते हैं और ग्रहण के साथ प्रयास कर सकते हैं । जैसा कि मैंने उल्लेख किया है, कुछ चीजें जो शुरुआती लोगों की मदद करने वाली हैं, वे अधिक अनुभवी डेवलपर्स के रास्ते में आते हैं।


जबकि मुझे लगता है कि एक स्केच को कई फाइलों (टैब या शामिल) में अलग करने से सब कुछ अपनी जगह पर होने में मदद मिलती है, मुझे ऐसा लगता है कि एक ही चीज़ (.h और .cpp) की देखभाल के लिए दो फाइलों का होना एक तरह का है। अनावश्यक अतिरेक / दोहराव। ऐसा लगता है कि कक्षा को दो बार परिभाषित किया जा रहा है, और हर बार मुझे एक जगह बदलने की आवश्यकता है, मुझे दूसरे को बदलने की आवश्यकता है। ध्यान दें कि यह केवल मेरे जैसे सरल मामलों पर लागू होता है, जहां किसी दिए गए हेडर का केवल एक कार्यान्वयन होगा, और वे केवल एक बार (एकल स्केच में) उपयोग करने जा रहे हैं।
हेल्टनबाइकर

यह संकलक / लिंकर के काम को सरल करता है और आपको उन .cpp फ़ाइल तत्वों को रखने की अनुमति देता है जो कक्षा का हिस्सा नहीं हैं, लेकिन कुछ विधि में उपयोग किए जाते हैं। और यदि क्लास में स्टैटिक मेमर हैं, तो आप उन्हें .h फ़ाइल में नहीं रख सकते।
इगोर स्टोपा

अलग .h और .cpp फ़ाइलों को लंबे समय तक अप्रमाणित माना जाता है। जावा, सी #, जेएस को किसी को हेडर फ़ाइलों की आवश्यकता नहीं है, और यहां तक ​​कि सीपीपी आईएसओ मानक उनसे दूर जाने की कोशिश कर रहे हैं। समस्या यह है कि बहुत अधिक विरासत कोड है जो इस तरह के आमूल परिवर्तन से टूट सकता है। यही कारण है कि हमारे पास सी के बाद सीपीपी है, और सिर्फ एक विस्तारित सी नहीं है। मुझे उम्मीद है कि सीपीपी के बाद सीपीसी फिर से वही होगा?

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

6

मैं एक पूर्णता के लिए एक उत्तर पोस्ट कर रहा हूं , एक हेडर का उपयोग किए बिना, एक ही .cpp फ़ाइल में एक वर्ग घोषित करने और लागू करने के तरीके का पता लगाने और परीक्षण करने के बाद । तो, मेरे प्रश्न के सटीक वाक्यांशों के बारे में "मुझे कक्षाओं का उपयोग करने के लिए कितने फ़ाइल प्रकारों की आवश्यकता है", वर्तमान उत्तर में इन फ़ाइलों का उपयोग किया जाता है: एक .ino जिसमें शामिल हैं, सेटअप और लूप, और .cpp युक्त संपूर्ण (बल्कि न्यूनतर)। ) वर्ग, एक खिलौना वाहन के मोड़ संकेतों का प्रतिनिधित्व करता है।

Blinker.ino

#include <TurnSignals.cpp>

TurnSignals turnSignals(2, 4, 8);

void setup() { }

void loop() {
  turnSignals.run();
}

TurnSignals.cpp

#include "Arduino.h"

class TurnSignals
{
    int 
        _left, 
        _right, 
        _buzzer;

    const int 
        amberPeriod = 300,

        beepInFrequency = 600,
        beepOutFrequency = 500,
        beepDuration = 20;    

    boolean
        lightsOn = false;

    public : TurnSignals(int leftPin, int rightPin, int buzzerPin)
    {
        _left = leftPin;
        _right = rightPin;
        _buzzer = buzzerPin;

        pinMode(_left, OUTPUT);
        pinMode(_right, OUTPUT);
        pinMode(_buzzer, OUTPUT);            
    }

    public : void run() 
    {        
        blinkAll();
    }

    void blinkAll() 
    {
        static long lastMillis = 0;
        long currentMillis = millis();
        long elapsed = currentMillis - lastMillis;
        if (elapsed > amberPeriod) {
            if (lightsOn)
                turnLightsOff();   
            else
                turnLightsOn();
            lastMillis = currentMillis;
        }
    }

    void turnLightsOn()
    {
        tone(_buzzer, beepInFrequency, beepDuration);
        digitalWrite(_left, HIGH);
        digitalWrite(_right, HIGH);
        lightsOn = true;
    }

    void turnLightsOff()
    {
        tone(_buzzer, beepOutFrequency, beepDuration);
        digitalWrite(_left, LOW);
        digitalWrite(_right, LOW);
        lightsOn = false;
    }
};

1
यह जावा की तरह है और कक्षा की घोषणा में विधियों के कार्यान्वयन को थप्पड़ मारता है। कम पठनीयता के अलावा - हेडर आपको संक्षिप्त रूप में विधियों की घोषणा देता है - मुझे आश्चर्य है कि यदि अधिक असामान्य वर्ग घोषणाएं (जैसे स्टैटिक्स, दोस्तों आदि के साथ) अभी भी काम करेंगी। लेकिन इस उदाहरण में से अधिकांश वास्तव में अच्छा नहीं है, क्योंकि इसमें केवल एक बार शामिल किए जाने के बाद फ़ाइल शामिल होती है। वास्तविक समस्या तब शुरू होती है जब आप एक ही फ़ाइल को कई स्थानों पर शामिल करते हैं और आपको लिंकर से परस्पर विरोधी ऑब्जेक्ट घोषणाएं मिलनी शुरू हो जाती हैं।
इगोर स्टोपा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.