Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Arduino]Overerf probleem

Pagina: 1
Acties:

  • 4Real
  • Registratie: Juni 2001
  • Laatst online: 14-09-2024
Ik wil mijn arduino zo programmeren dat uitbouw van sensoren in de toekomst makkelijker zal zijn. Voor nu heb ik twee sensoren (een LDR en een Thermometer) en deze heb ik als volgt omgezet naar classes:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Sensor {
  public: 
    // fields
    String Name;
    // constructors
    Sensor();
    Sensor(uint8_t pin, String Name);
    // methods
    virtual void Update();
    int GetValue();
  protected:
    uint8_t _pin;
    int _Value;
};

class TemperatureSensor : public Sensor {
  public:
    // constructor
    TemperatureSensor(uint8_t pin, String name)
      : Sensor(pin, name) {
    }
    // methods
    float GetTemperature();
};

class LdrSensor : public Sensor {
  public:
    // constructor
    LdrSensor(uint8_t pin, String name)
      : Sensor(pin, name) {
    }
};


En neem ik als volgt op in een array, zodat ik ze in één loop kan update.
C:
1
2
sensorList[0] = new TemperatureSensor(0, "livingroom");
sensorList[1] = new LdrSensor(2, "livingroom");


Het updaten is geen probleem aangezien ik de functie Sensor::Update(); daarvoor gebruik, maar wanneer ik de waardes van de sensoren wil gaan printen loop ik tegen problemen aan. Wat ik wil is dat de LDR gebruikt maar van de functie GetValue (van de baseclass Sensor dus) en bij TemperatureSensor wil ik de GetTemperature gebruiken. Bij de laatste is namelijk omzetten naar graden Celsius nodig.

Nu heb ik met dynamic_cast zitten stoeien, maar ik begrijp niet wat er nou precies mis gaat.

Ik zal mijn probeersels neerzetten met de daarbij horende foutmeldingen:
C:
1
TemperatureSensor * n = dynamic_cast<TemperatureSensor*>( &sensorList[i] );

error: cannot dynamic_cast '& sensorList[ i]' (of type 'class Sensor**') to type 'class TemperatureSensor*' (source is not a pointer to class)


C:
1
TemperatureSensor* n = dynamic_cast<TemperatureSensor*>( sensorList[i] );

error: 'dynamic_cast' not permitted with -fno-rtti


Deze foutmelding zit volgens mij dichtste bij de oplossing, wanneer ik de voorbeelden zie op internet, maar hier heb ik geen rechten en de IDE van Arduino geeft hier ook niet echt mogelijkheden voor. Tevens komt dit probleem amper voor op internet.

Als ik uit wanhoop een static_cast gebruik, gaat het casten goed, maar bij aanroepen van de functie krijg ik alsnog een foutmelding.
C:
1
2
3
4
TemperatureSensor* n = static_cast<TemperatureSensor*>( &(*sensorList[i]) );

// deze functie gaat verkeerd.
n.GetTemperature();

error: request for member 'GetTemperature' in 'n', which is of non-class type 'TemperatureSensor*'


Nu valt mij op dat hij hier niet TemperatureSensor noemt als class, maar TemperatureSensor* (met asterisk).

En ik zie nu even niet meer in wat ik nou verkeerd doe. Als ik dit in c# zou doen zou het als volgt uitzien:
C#:
1
2
3
4
if(sensor is TemperatureSensor )
{
   TemperatureSensor temp = (TemperatureSensor)sensor;
}


Maar dit krijg ik niet voor mekaar bij een Arduino. Ik zal vast wel iets verkeerd doen, maar zie even niet wat.

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 20:57

Matis

Rubber Rocket

Hoe definieer jij de sensorList variabele?

If money talks then I'm a mime
If time is money then I'm out of time


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Eerst en vooral je probleem:
C++:
1
2
3
4
TemperatureSensor* n = static_cast<TemperatureSensor*>( &(*sensorList[i]) );

// gebruik -> ipv .
n->GetTemperature();


Maar om de bal even terug te kaatsen:
Overerven zoals je het nu doet heeft geen zin. Het doel zou moeten zijn dat je enkel nog Sensor aanroepen doet. Het moment dat je dynamic_casts nodig hebt als je over een lijst van base-classes loopt moet je je beginnen afvragen of je niet iets fout doet.

Er zijn, afhankelijk van je uiteindelijke doel wel enkele mogelijkheden:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Sensor {
  virtual int GetValue() { return GetValueImpl(); }
  virtual void Format(char[] buffer, const int len) { snprintf(buffer, len, "%d", GetValue()); } // schrijft geformatteerde waarde weg, bvb: '10°C' of '5'
  virtual void GetThresholds(int& min, int& max) { min = INT_MIN, max = INT_MAX; }
  bool Alarmed() const { int min, max; GetThresholds(min, max); int value = GetValue(); return value < min || value > max; }
protected:
  int GetValueImpl() { ... };
};

class TempSensor : public Sensor {
  virtual int GetValue() { return 1000 * GetValueImpl() *... / ... + ... ...; }
  virtual void GetThresholds(int& min, int& max) { min = -10; max = +50; }
  virtual void Format(...) { snprintf(buffer, len, "%d°C", GetValue()); }
}

for each sensor in sensorlist {
  char buf[32];
  sensor->Format(buf, 32);
  printf("%s: %s %s", sensor->GetName(), buf, sensor->Alarmed() ? "(alarm)":"");
}

bijvoorbeeld... Maar ik zou je aanraden om de C-isms (printf/snprintf/min/max) eruit te halen en te vervangen door de nodige C++ constructs zoals stringstreams, pair<int,int>, etc.

[ Voor 5% gewijzigd door H!GHGuY op 15-06-2013 11:11 ]

ASSUME makes an ASS out of U and ME


  • 4Real
  • Registratie: Juni 2001
  • Laatst online: 14-09-2024
Matis schreef op zaterdag 15 juni 2013 @ 10:49:
Hoe definieer jij de sensorList variabele?
C:
1
2
#define SENSORLISTLENGTH 2 
Sensor *sensorList[SENSORLISTLENGTH];


De update functie ziet er dan als volgt uit:
C:
1
2
3
for ( int i = 0; i < SENSORLISTLENGTH; i++ ) {
  (*sensorList[i]).Update();
}

En tot hier werkt alles nog gewoon zoals ik wil dat het moet werken.
H!GHGuY schreef op zaterdag 15 juni 2013 @ 11:09:
Eerst en vooral je probleem:
C++:
1
2
3
4
TemperatureSensor* n = static_cast<TemperatureSensor*>( &(*sensorList[i]) );

// gebruik -> ipv .
n->GetTemperature();


Maar om de bal even terug te kaatsen:
Overerven zoals je het nu doet heeft geen zin. Het doel zou moeten zijn dat je enkel nog Sensor aanroepen doet. Het moment dat je dynamic_casts nodig hebt als je over een lijst van base-classes loopt moet je je beginnen afvragen of je niet iets fout doet.
Mijn idee was om een lijst te definieren die gebasseerd in op de base-class Sensor, waar ik dan derived classes in zet, zodat ze in één loop kan updaten en ook in één loop kan weergeven. Waar uitzonderingen nodig zijn (zoals bij de TemperatuurSensor) kan ik dan een andere functie aanroepen om de waarde te krijgen die ik wil hebben.

Voor het printen van waardes was dit mijn idee (even in c# voor het idee)

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Sensor
{
    public int Value { get; protected set; }

    public void Update()
    {
        this.Value = 0; // analogRead(pinNr)
    }
}

class LdrSensor : Sensor
{

}

class TemperatureSensor : Sensor
{
    public float GetTemperature()
    {
        return this.Value / 2.5f;
    }
}

// definieren van de sensor lijst
List<Sensor> sensorList = new List<Sensor>();
sensorList.Add(new LdrSensor());
sensorList.Add(new TemperatureSensor());

// en dan in de print functie
foreach (Sensor sensor in sensorList)
{
    if (sensor is TemperatureSensor)
    {
        TemperatureSensor thermometer = (TemperatureSensor)sensor;

        Serial.println("Temperatuur = " + (String)thermometer.GetTemperature() + " graden celcius");
    }
    else
    {
        Serial.println("Sensor: '" + (String)sensor.Name + "' = " + (String)sensor.Value);
    }
}

Dit is het idee dat ik in mijn hoofd heb zitten en probeer op mijn Arduino werkt te krijgen, maar bij het laatste onderdeel om te kijken waar een klass van overerft om daar mogelijke uitzonderingen op te maken daar gaat het op de Arduino mis. Echter heb ik het idee dat mijn oplossing wel goed is.

  • justahuman
  • Registratie: Maart 2011
  • Laatst online: 23:51
C++ classes hebben een huge overhead zeker op een arduino, wat je beter kan doen is een standaard functies maken die elke sensor file implementeerd en dan de functie pointers aan de update array mee geven. dan kan je gewoon Array[0]() doen.

  • 4Real
  • Registratie: Juni 2001
  • Laatst online: 14-09-2024
justahuman schreef op zaterdag 15 juni 2013 @ 12:36:
C++ classes hebben een huge overhead zeker op een arduino, wat je beter kan doen is een standaard functies maken die elke sensor file implementeerd en dan de functie pointers aan de update array mee geven. dan kan je gewoon Array\[0]() doen.
Als snelle oplossing denk ik ook dat ik dat ga doen. Per sensor een class en array, zodat ik geen complex trucks hoef uit te halen.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
Je zou de print functies naar je sensor class kunnen halen net zoals je Update(), of hiervoor externe print functies maken ( of operator<< definieren ihgv streams ).

Iig kun je denk ik de GetValue() niet makkelijk in je base kwijt aangezien sensoren niet allemaal hetzelfde value type produceren ( denk aan een analoge vs digitale sensor, of een float versus een int )
C++ classes hebben een huge overhead zeker op een arduino
Wat noem jij huge? Dacht dat Arduinio juist C++ georienteerd was en nu vertel je dat je beter geen classes kunt gebruiken?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • justahuman
  • Registratie: Maart 2011
  • Laatst online: 23:51
farlane schreef op zaterdag 15 juni 2013 @ 13:26:
Je zou de print functies naar je sensor class kunnen halen net zoals je Update(), of hiervoor externe print functies maken ( of operator<< definieren ihgv streams ).

Iig kun je denk ik de GetValue() niet makkelijk in je base kwijt aangezien sensoren niet allemaal hetzelfde value type produceren ( denk aan een analoge vs digitale sensor, of een float versus een int )


[...]

Wat noem jij huge? Dacht dat Arduinio juist C++ georienteerd was en nu vertel je dat je beter geen classes kunt gebruiken?
Arduino zelf gebruikt maar een klein gedeelte van de C++ standaard, je moet het niet overenigineren. Functies groeperen enz kan op zich nog wel maar overdrijf het niet met ingewikkelde overerf structuren.

  • Adion
  • Registratie: Januari 2001
  • Laatst online: 15:15
code:
1
TemperatureSensor * n = dynamic_cast<TemperatureSensor*>( &sensorList[i] );

code:
1
Sensor *sensorList[SENSORLISTLENGTH];

Je sensorList is al een array van pointers.
Bij je dynamic_cast neem je van die pointer het adres, en het resultaat is dus een (Sensor**) die je probeert te casten naar een (TemperatureSensor*)
Gewoon de & weghalen, zodat je een (Sensor*) naar een (TemperatureSensor*) cast dus.

VirtualDJ 2024 - Fast Image Resizer - Instagram


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
justahuman schreef op zaterdag 15 juni 2013 @ 16:32:

Arduino zelf gebruikt maar een klein gedeelte van de C++ standaard, je moet het niet overenigineren. Functies groeperen enz kan op zich nog wel maar overdrijf het niet met ingewikkelde overerf structuren.
Van overengineering is hier geen sprake; er is een base class en wat afgeleide klassen.

dynamic_cast<> echter werkt alleen als rtti aanstaat (zoals de foutmelding ook duidelijk maakt ) en dat zal in het algemeen niet het geval zijn op een embedded platform aagezien dat wel behoorlijk wat overhead heeft.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 23-11 03:08
farlane schreef op zaterdag 15 juni 2013 @ 13:26:
Dacht dat Arduinio juist C++ georienteerd was en nu vertel je dat je beter geen classes kunt gebruiken?
Even voor het perspectief: een Arduino heeft ergens rond de 2k-8k geheugen (kilobytes, ja). Dan telt elke byte, natuurlijk. Overigens komen RTTI en vtables niet in het (beperkte) RAM terecht maar in het flashgeheugen; daar heb je iets meer ruimte (al moet je het niet al te gek maken — bijvoorbeeld exception handling vergroot de ruimte die je voor je code nodig hebt nogal). Qua RAM heeft een C++ class vooral de overhead van een vtable-pointer.

[ Voor 32% gewijzigd door Soultaker op 15-06-2013 19:08 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 13:12
Soultaker schreef op zaterdag 15 juni 2013 @ 19:07:
Even voor het perspectief: een Arduino heeft ergens rond de 2k-8k geheugen (kilobytes, ja). Dan telt elke byte, natuurlijk. Overigens komen RTTI en vtables niet in het (beperkte) RAM terecht maar in het flashgeheugen; daar heb je iets meer ruimte (al moet je het niet al te gek maken — bijvoorbeeld exception handling vergroot de ruimte die je voor je code nodig hebt nogal). Qua RAM heeft een C++ class vooral de overhead van een vtable-pointer.
Ik ben bekend met de beperkingen van de (achterhaalde) hardware van de originele Arduino, daarom zou ik altijd voor de moderne 32bit compatible versies van bijvoorbeeld Olimex gaan met C++ zonder de Arduino overhead. ( Ik geloof dat ze nu zelf wel een Atmel Cortex-M3 versie hebben gemaakt ) zodat je niet onnodig ligt te wurgen met de beperkingen van de jaren 90 architectuur van de ATmega.

Normaal gesproken als je C++ op een dergelijk platform draait zullen RTTI en exception handling uitstaan. Blijft de overhead van de vtable(pointer), maar als je daarmee niet kunt leven is het misschien handiger om gewoon C te pakken.

Ik zou eerder het gebruik van de heap proberen te vermijden, dat levert in de praktijk meer problemen op...

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1