Pages: [1]   Go Down
Author Topic: Best way to reference another class library  (Read 954 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello

I'm a c# guy, so I need some opinions on how to best accomplish this in arduino c/c++:

I have a class library (is that the correct term? A .h and .cpp file that defines a class). Let's call it ClassA. And then I have another class library (from an external part), lets call it ClassB.

ClassA is to use some methods in ClassB in a few places.

What I've done now is, in ClassA i included ClassB.h, and in the constructor of ClassA I pass an instance of ClassB that is stored as a private variable in ClassA. Like so:

Code:
ClassA.h:
<include ClassB.h>
private:
   ClassB _b

ClassA.cpp:
<include ClassB.h>
ClassA::ClassA(ClassB b)
{
   _b = b;
}
ClassA::MethodX()
{
   _b.doSomething(...);
}

Is this a good way of doing it? Will _b in ClassA be a reference to the instance of ClassB that is passed?

Is there a better way of doing this?

I'm thinking it would be neater to pass a reference to the method of ClassA, instead of the class, so then ClassA could be any method of any object that does the work I want... And then ClassA wouldn't need to have a reference to ClassB. But I have not managed to figure out how to do this, pass a reference and store the reference to a method in a private variable, there's too much * and & and () going on...

Thanks!
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Is this a good way of doing it?

No.

Will _b in ClassA be a reference to the instance of ClassB that is passed?

No, because you haven't declared the variable as a reference (that is there is no "&" symbol there). It will be a copy, and how good a copy depends on your copy constructor, or your operator=.

Is there a better way of doing this?

Almost certainly. What are you ultimately trying to achieve here?

You might want to check this out:

http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Haha, ok then!

Quote
What are you ultimately trying to achieve here?
smiley-lol

Ok, let's be a little more straightforward.

I am using a shield that handles more pins than the regular arduino, so instead of the regular globally available digitalRead() I have to call XX.digitalRead(), a method of instance of the class that handles the pins for my shield.

I am developing some classes of my own to use, for example a Button class, that takes care of the state of the buttons for the pins of the shield, debouncing and stuff. So, I want to create an amount of instances of my button class, one for different pins, and then I want the button to read the pin, which has to be done via a call to XX.digitalRead().

So, that's what I'm trying to achieve.

So I realized i could use the keyword "extern XX" to achieve the same thing as the globally available digitalRead(), but that's a bit too ugly I think.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
So I realized i could use the keyword "extern XX" to achieve the same thing as the globally available digitalRead(), but that's a bit too ugly I think.
If XX is an instance of a class, that probably won't work, anyway.

Enough of the handwaving. Post your actual code, and we'll help you modify it is pass things properly by reference.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
If XX is an instance of a class, that probably won't work, anyway.

I tried it out, it did indeed work!

Thanks, ok, I'll try to get it sorted out a bit and post something.

I just thought this would be like a very obvious thing to do... one class that uses functionality provided by another... But maybe I'm thinking the wrong way.

Thanks a lot for helping me out guys.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I just thought this would be like a very obvious thing to do... one class that uses functionality provided by another... But maybe I'm thinking the wrong way.
No, that happens all the time. Generally, one class would get a pointer to an instance of the other class. Since pointers are limited on the Arduino (there being no new operator that would generally be used to create a pointer to an instance of a class), objects are created, rather than pointers to objects. Without a pointer to pass around, a reference to the object is passed around, instead.

References are a little more challenging to set up and use, but they are then almost the same as pointers (except for using . instead of ->).
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, so here is relevant parts of my code. I have removed irrelevant declarations, initialize functions and calls and so...

I'm using the Centipede shield, library available from here: http://macetech.com/Centipede.zip

Centipede.h has the following method:
Code:
class Centipede
{
public:
int digitalRead(int pin);
};

So, I have created a Button class.

Parts of SBButton.h

Code:
class SBButton
{
public:
SBButton(int pin);
boolean stateChanged();
private:
int _pin;
int _buttonState;
long _lastDebounceTime;
int _lastRead;
};

Parts of SBButton.cpp

Code:
SBButton::SBButton(int pin)
{
_pin = pin;
_buttonState = HIGH;
_lastRead = HIGH;
_lastDebounceTime = 0;
};

boolean SBButton::stateChanged()
{
extern Centipede CS;
int read = CS.digitalRead(_pin);
if (read != _lastRead)
{
_lastDebounceTime = millis();
}
if ((millis() - _lastDebounceTime) > _debounceDelay)
{
if (read != _buttonState)
{
_lastRead = read;
_buttonState = read;
return true;
}
}
_lastRead = read;
return false;
}

Parts of my scetch:

Code:
#include <Wire.h>
#include <Centipede.h>
#include <SBButton.h>

Centipede CS;
SBButton stepButtons[STEPCOUNT] = {SBButton(16),SBButton(31),SBButton(17),SBButton(30)};

(initialization etc removed....)

void loop() {
  for (int i = 0; i < STEPCOUNT; i++){
    if (stepButtons[i].stateChanged()){
        Serial.println("State changed");
    }
  }
}

So, the extern Centipede CS statement in SBButton::stateChanged gives me access to the Centipede instance in my scetch, but naturally it's a quite ugly solution. I would like to make something nicer and more reusable. On the other hand, if that comes with unreadable * & code or overhead, maybe I should settle with this, I guess it's quite efficient...

Thanks in advance
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
On the other hand, if that comes with unreadable * & code or overhead, maybe I should settle with this, I guess it's quite efficient...
Well, I guess "unreadable" is in the eye of the beholder.

Do you need access to the Centipede object in the sketch? If not, you could declare the Centipede object as a private class member, and not have to worry about passing the object to the SBButton instances.

If you do, then you need to pass (a reference to) the Centipede object to the SBButton constructor, and save that reference in the SBButton class, as a private member.

You can (more easily) create, in the SBButton class, a pointer to the Centipede object, and initialize that pointer in the constructor, from a reference:
Code:
// Header
class SBButton
{
   public:
      SBButton(Centipede *pCent, int pin);
   private:
      Centipede *_pCent;
      int _pin;
};

// Source
SBButton::SBButton(Centipede *pCent, int pin)
{
   _pCent = pCent;
   _pin = pin;
}

// Sketch
Centipede CS;
SBButton someButton(&CS, 14);

Finally, you could possibly derive your class from the Centipede class, so that SBButton would inherit the digitalRead() method. I don't know if this would be advantageous or not.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 474
Posts: 18696
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

New and delete can be implemented very easily, if that is all that is required:

Code:
void* operator new(size_t n, void * p) { return p;  }  // placement new
void* operator new(size_t n) { return malloc (n); }
void operator delete (void * p) { free (p); };

If you pass a pointer around, that might save a lot of mucking around.

As PaulS says, though, in a constructor of one class you can initialize a reference to another class, however you need to be aware of static initialization orders then.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
You can (more easily) create, in the SBButton class, a pointer to the Centipede object, and initialize that pointer in the constructor, from a reference

Great, that was exactly what I was looking for, tried to create this the other day, but I failed to put the *:s and &:s in the right place. :-) This is a nice clear example, super, thanks!

The Centipede class will be used by other classes I'm creating as well, a similar class like the button-class but for handling Led:s. And centipede has an initialize function that needs to be executed before the Buttons and Leds can start their work, so I guess doing this in the scetch and passing it on is good and flexible.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Tried it out, seems to work fine!

For future refence:

Accessing the methods of the referenced class instance in the example by PaulS has to be done with "->" not "."

Code:
int read = _pCent->digitalRead(_pin);

instead of

Code:
int read = _pCent.digitalRead(_pin);

Don't ask me why, yet, maybe later when I've gotten a bit more into c/c++
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Accessing the methods of the referenced class instance in the example by PaulS has to be done with "->" not "."
I'd have mentioned that, but I thought it was obvious. I've been using C++ for 15 years, and I still don't have a good answer for why dereferencing an object and dereferencing a pointer use different operators. They just do.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 40
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yeah, well, to a c#/javascript guy like me, pointers and references, the * & syntax, and things like -> instead of . is a bit tricky.But I realize that is because it is advanced stuff that offers great potential, I just need to study it some more.

But then, the fact that arduino is "a bit" different, and lacks "new" (and other things?) makes me go hmm, is it c, c++ or arduino/avr c/c++ I am supposed to study... What code examples that I find out there works in the arduino and what does not, you know, and the arduino manual is a dead end when it comes to describing this.
Logged

Pages: [1]   Go Up
Jump to: