Sınıflar ve nesneler: bunları kullanmak için kaç tane dosya türüne ihtiyacım var?


20

C ++ veya C ile daha önce deneyime sahip değilim, ancak C # programlamayı biliyorum ve Arduino öğreniyorum. Sadece taslaklarımı düzenlemek istiyorum ve kısıtlamaları ile bile Arduino diliyle oldukça rahatım, ama Arduino programıma gerçekten nesne odaklı bir yaklaşım istiyorum.

Bu nedenle, kodu düzenlemek için aşağıdaki yollara (kapsamlı liste değil) sahip olabileceğinizi gördüm:

  1. Tek bir .ino dosyası;
  2. Aynı klasördeki birden fazla .ino dosyası (IDE'nin "sekmeler" olarak adlandırdığı ve görüntülediği);
  3. Aynı klasörde .h ve .cpp dosyası bulunan bir .ino dosyası;
  4. Yukarıdaki ile aynıdır, ancak dosyalar Arduino program klasörü içinde kurulu bir kütüphanedir.

Ayrıca aşağıdaki yolları da duydum, ancak henüz çalışmalarını sağlamadım:

  • Aynı tek bir .ino dosyasında bir C ++ tarzı sınıf bildirmek (duymuş, ancak hiç çalışmadı - bu bile mümkün mü?);
  • [tercih edilen yaklaşım] Bir sınıf bildirilen bir .cpp dahil, ancak olmayan bir .h dosyasını kullanarak (gerektiği bu iş bu yaklaşımı istiyorum?);

Kodları daha bölümlenmiş olacak şekilde sınıfları kullanmak istediğimi unutmayın, uygulamalarım çok basit olmalı, sadece düğmeler, ledler ve buzzerler çoğunlukla.


İlgilenenler için, burada başsız (sadece cpp) sınıf tanımları hakkında ilginç bir tartışma var: programmers.stackexchange.com/a/35391/35959
heltonbiker

Yanıtlar:


31

IDE işleri nasıl organize ediyor?

İlk olarak, IDE "çiziminizi" şu şekilde düzenler:

  • Ana .ino. Dosyası, için, So bulunduğu klasörün aynı adı biridir foobar.inoiçinde foobarklasörde - Ana dosya foobar.ino olduğunu.
  • .inoBu klasördeki diğer dosyalar, ana dosyanın sonunda alfabetik sırayla birleştirilir (ana dosyanın nerede olduğuna bakılmaksızın alfabetik olarak).
  • Bu birleştirilmiş dosya bir .cppdosya (örn. foobar.cpp) Haline gelir - geçici bir derleme klasörüne yerleştirilir.
  • Önişlemci "yardımcı olarak" bu dosyada bulduğu işlevler için işlev prototipleri oluşturur.
  • Ana dosya #include <libraryname>yönergeler için taranır . Bu, IDE'yi her bir (belirtilen) kitaplıktaki tüm ilgili dosyaları geçici klasöre kopyalamak ve bunları derlemek için talimatlar oluşturmak üzere tetikler.
  • Herhangi .c, .cppya .asmkroki klasördeki dosyaları (olduğunu, bunlar ayrı dosyalar olarak normal şekilde derlenmiştir) ayrı derleme birimleri olarak inşa sürecine eklenir
  • Tüm .hdosyalar da geçici derleme klasörüne kopyalanır, böylece .c veya .cpp dosyalarınız tarafından başvurulabilir.
  • Derleyici derleme işlemi standart dosyalarına ekler (gibi main.cpp)
  • Derleme işlemi, yukarıdaki tüm dosyaları nesne dosyalarına derler.
  • Derleme aşaması başarılı olursa, bunlar AVR standart kitaplıklarıyla (örneğin, size vermek strcpyvb.) Birlikte bağlanır.

Tüm bunların bir yan etkisi, ana çizimin (.ino dosyaları) tüm amaç ve amaçlar için C ++ olduğunu düşünebilirsiniz. Ancak işlev prototipi oluşturma, dikkatli değilseniz karanlık hata mesajlarına yol açabilir.


İşlemci öncesi tuhaflıklardan kaçınma

Bu özelliklerden kaçınmanın en basit yolu ana çiziminizi boş bırakmak (ve başka .inodosyalar kullanmamak ). Sonra başka bir sekme (bir .cppdosya) yapın ve dosyalarınızı şu şekilde içine yerleştirin:

#include <Arduino.h>

// put your sketch here ...

void setup ()
  {

  }  // end of setup

void loop ()
  {

  }  // end of loop

Eklemeniz gerektiğini unutmayın Arduino.h. IDE bunu ana çizim için otomatik olarak yapar, ancak diğer derleme birimleri için bunu yapmanız gerekir. Aksi takdirde String, donanım kayıtları vb.


Kurulumdan / ana paradigmadan kaçınma

Setup / loop konseptiyle çalıştırmak zorunda değilsiniz. Örneğin, .cpp dosyanız şunlar olabilir:

#include <Arduino.h>

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

Kütüphaneye dahil edilmeyi zorla

"Boş çizim" konseptiyle çalıştırıyorsanız, projenin başka bir yerinde, örneğin ana .inodosyanızda kullanılan kütüphaneleri de eklemeniz gerekir :

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

Bunun nedeni, IDE'nin ana dosyayı yalnızca kitaplık kullanımı için taramasıdır. Ana dosyayı etkin olarak, hangi harici kütüphanelerin kullanımda olduğunu belirleyen bir "proje" dosyası olarak düşünebilirsiniz.


Adlandırma sorunları

  • Ana çiziminize "main.cpp" adını vermeyin - IDE kendi main.cpp dosyasını içerir, böylece bunu yaparsanız bir kopyasına sahip olursunuz.

  • .Cpp dosyanızı ana .ino dosyanızla aynı adla adlandırmayın. .İno dosyası etkin bir şekilde bir .cpp dosyası haline geldiğinden, bu size bir ad çakışması da verir.


Aynı tek bir .ino dosyasında bir C ++ tarzı sınıf bildirmek (duymuş, ancak hiç çalışmadı - bu bile mümkün mü?);

Evet, bu TAMAM derler:

class foo {
  public:
};

foo bar;

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

Bununla birlikte, muhtemelen normal uygulamayı takip etmek için en iyisiniz: Bildirimlerinizi .hdosyalara ve tanımlarınızı (uygulamalarınızı) .cpp(veya .c) dosyalara koyun .

Neden "muhtemelen"?

Örneğim gibi her şeyi tek bir dosyada bir araya getirebilirsiniz . Daha büyük projeler için daha organize olmak daha iyidir. Sonunda, işleri "kara kutulara" ayırmak istediğiniz orta ila büyük boyutlu bir projede sahneye çıkıyorsunuz - yani, bir şeyi yapan, iyi yapan, test edilen ve kendi kendine yeten bir sınıf ( olabildiğince uzağa).

Bu sınıf daha sonra projenizdeki diğer birden fazla dosyada kullanılırsa, ayrı .hve .cppdosyaların oynatıldığı yer burasıdır .

  • .hDosya beyan sınıf - olduğunu, bunun neler işlevleri ne yaptığını bilmek diğer dosyalar için yeterli ayrıntı sağlar ve nasıl denir.

  • .cppDosya tanımlar (uygular) sınıfı - sınıfını yapmak, aslında fonksiyonlar ve statik sınıf üyelerini içerir, onun şey. Yalnızca bir kez uygulamak istediğiniz için, bu ayrı bir dosyadadır.

  • .hDosya diğer dosyalar dahil olur şeydir. .cppDosya sınıfı fonksiyonlarını uygulamak amacıyla IDE tarafından bir kez derlenmektedir.

Kütüphaneler

Bu paradigmayı izlerseniz, tüm sınıfı ( .hve .cppdosyaları) kolayca bir kütüphaneye taşımaya hazırsınız demektir . Daha sonra birden fazla proje arasında paylaşılabilir. Tek yapmanız gereken bir klasör (örn. myLibrary) Oluşturmak ve .hve .cppdosyalarını içine yerleştirmek (örn. myLibrary.hVe myLibrary.cpp) ve daha sonra bu klasörü librariesskeçlerinizin bulunduğu klasörde (eskiz defteri klasörü) klasörünüze koymaktır .

IDE'yi yeniden başlatın ve artık bu kitaplığı biliyor. Bu gerçekten çok basit ve şimdi bu kütüphaneyi birden fazla proje üzerinde paylaşabilirsiniz. Bunu çok yapıyorum.


Daha detay bit burada .


Güzel cevap. Herkes diyor neden "sen şunlardır: Tek en önemli konu olsa da, henüz bana belli oldu vermedi muhtemelen normal bir uygulama izlemenin en iyi off: .h + .cpp"? Neden daha iyi? Muhtemelen neden ? Ve en önemlisi: Ben ne kadar yok o olduğunu, her iki arayüz var ve aynı tek .cpp dosyasında uygulama (thas olduğunu bütün sınıf kodu)? Şimdilik çok teşekkür ederim! : o)
heltonbiker

Neden "büyük olasılıkla" ayrı dosyalarınız olması gerektiğini yanıtlamak için birkaç paragraf daha ekledik.
Nick Gammon

1
Nasıl mı değil bunu? Hepsini yanıtımda gösterildiği gibi bir araya getirin, ancak ön işlemcinin size karşı çalıştığını görebilirsiniz. Bazı mükemmel geçerli C ++ sınıf tanımları, ana .ino dosyasına yerleştirilirse başarısız olur.
Nick Gammon

Ayrıca .cpp dosyalarınızın ikisine bir .H dosyası eklerseniz ve bu .h dosyası bazılarının ortak bir alışkanlığı olan kod içeriyorsa da başarısız olurlar. Açık kaynağı, sadece kendiniz düzeltin. Bunu yapmaktan çekiniyorsanız, muhtemelen açık kaynak kullanmamalısınız. @Nick Gammon, şimdiye kadar gördüğüm her şeyden daha iyi bir açıklama.

@ Spiked3 En rahat olduğum şeyi seçmek o kadar da önemli değil, şimdilik, ilk etapta seçim yapabileceğim şeyi bilmek meselesi. Seçeneklerimin ne olduğunu ve her seçeneğin neden böyle olduğunu bilmiyorsam nasıl mantıklı bir seçim yapabilirim? Dediğim gibi, C ++ ile daha önce bir deneyimim yok ve bu cevapta gösterildiği gibi, Arduino'daki C ++ ekstra bakım gerektirebilir. Ama eminim sonunda onu kavramak ve tekerleği yeniden icat etmeden
işimi hallediyorum

6

Benim tavsiyem, her şey için .h ve .cpp dosyalarına ayrı arayüz ve uygulama yapmak gibi tipik C ++ yöntemlerine bağlı kalmaktır.

Birkaç yakalama var:

  • en az bir .ino dosyasına ihtiyacınız var - Ben sınıfları örnek nerede .cpp dosyasına bir symlink kullanın.
  • Arduino ortamının beklediği geri çağrıları sağlamalısınız (setu, loop, vb.)
  • bazı durumlarda, Arduino IDE'yi normal olandan ayıran, bazı kütüphanelerin otomatik olarak dahil edilmesi gibi, standart olmayan garip şeylerden şaşıracaksınız, ancak diğerleri değil.

Veya Arduino IDE'yi terk edebilir ve Eclipse ile deneyebilirsiniz . Bahsettiğim gibi, yeni başlayanlara yardım etmesi gereken bazı şeyler, daha deneyimli geliştiricilerin yoluna girme eğilimindedir.


Bir çizimi birden çok dosyaya (sekmeler veya ekler) ayırmanın her şeyin kendi yerinde olmasına yardımcı olduğunu hissediyorum, ancak aynı şeyle (.h ve .cpp) ilgilenmek için iki dosyaya ihtiyacım var gibi hissediyorum gereksiz artıklık / çoğaltma. Sınıf iki kez tanımlanıyor gibi hissettiriyor ve her yer değiştirmem gerektiğinde diğerini değiştirmem gerekiyor. Bu, yalnızca belirli bir üstbilginin yalnızca bir uygulamasının yapılacağı ve yalnızca bir kez (tek bir çizimde) kullanılacağı benimki gibi basit durumlar için geçerlidir.
heltonbiker

Derleyici / bağlayıcı çalışmasını basitleştirir ve sınıfın bir parçası olmayan, ancak bazı yöntemlerde kullanılan .cpp dosyaları öğelerine sahip olmanızı sağlar. Sınıfın statik memer'ları varsa, bunları .h dosyasına yerleştiremezsiniz.
Igor Stoppa

.H ve .cpp dosyalarının ayrılması uzun zamandır gereksiz olarak kabul edildi. Java, C #, JS hiçbiri başlık dosyaları gerektirmez ve cpp iso standartları bile onlardan uzaklaşmaya çalışıyor. Sorun şu ki, böyle radikal bir değişimle kırılabilecek çok fazla eski kod var. C'den sonra CPP'ye sahip olmamızın nedeni, sadece genişletilmiş bir C değil. Aynı şeyin tekrar olacağını umuyorum, CPP'den sonra CPX?

Tabii ki, bir sonraki revizyon, üstbilgiler olmadan, üstbilgiler olmadan yapılan aynı görevleri gerçekleştirmenin bir yolunu bulursa ... ancak bu arada üstbilgiler olmadan yapılamayan birçok şey var: Derlemenin nasıl dağıtıldığını görmek istiyorum başlıklar olmadan ve büyük genel masraflar olmadan gerçekleşebilir.
Igor Stoppa

6

Üstbilgi kullanmadan aynı .cpp dosyasındaki bir sınıfı bildirme ve uygulama yöntemini bulup test ettikten sonra, yalnızca eksiksizlik için bir yanıt gönderiyorum . Yani, "sınıfları kullanmak için kaç dosya türüne ihtiyacım var" sorusunun tam olarak ifade edilmesiyle ilgili olarak, bu cevap iki dosya kullanır: bir include, setup ve loop içeren bir .ino ve bütünü içeren .cpp (oldukça minimalist) ) sınıf, bir oyuncak aracın dönüş sinyallerini temsil eder.

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
Bu java gibidir ve yöntemlerin sınıf ilanına uygulanmasını tokatlar. Daha az okunabilirlik dışında - başlık size yöntemlerin kısa bir biçimde bildirilmesini sağlar - Daha sıra dışı sınıf bildirimlerinin (statik, arkadaşlar vb. Gibi) hala işe yarayıp yaramayacağını merak ediyorum. Ancak, bu örneğin çoğu iyi değildir, çünkü dosyayı yalnızca bir dahil etme işlemi bittikten sonra içerir . Gerçek sorunlar, aynı dosyayı birden çok yere eklediğinizde ve bağlayıcıdan çakışan nesne bildirimleri almaya başladığınızda başlar.
Igor Stoppa
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.