Loading...
Pages: 1 [2] 3   Go Down
Author Topic: Passing I/O functions to a class.  (Read 773 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hmm, but can I check a stream for input in a non blocking way?
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
MyClass * pClass = new MyClass(var1, var2);
pClass->Init();
Thanks, that was helpful.

A safer way would be to use reference like MyClass & rClass; but this requires that you pass the reference via the constructor. It would be too late in the Init() function.
I do not see a problem with that.

As for function pointers, I would rather use derived classes.
Like declaring Menu class based on serial class?
(Just learned that Serial is not the class, so maybe it is 'serial'?).

But what if the menu wants to read from Serial, but display informations somewhere else and then the errors on...?
Logged

Temple, Texas
Offline Offline
Sr. Member
****
Karma: 14
Posts: 354
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@OutOfLine

Not sure where in this thread it fits, but on the Arduino I usually use the Streaming Library, like this:
Code:
#include <Streaming>
...
Serial << "Hello there " << ", i= " << i << endl;
...

And then when I compile for various reasons on another platform I use:
Code:
#include <iostream>
using namespace std;
ostream & Serial = cout;

... so now I can use throughout, the same

Serial << "Hello there " << ", i= " << i << endl;

Cheers,
John
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@johncc:
Thank you for the nice hint, will try it later on.
Logged

Finland
Offline Offline
Jr. Member
**
Karma: 1
Posts: 84
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Like declaring Menu class based on serial class?
(Just learned that Serial is not the class, so maybe it is 'serial'?).

But what if the menu wants to read from Serial, but display informations somewhere else and then the errors on...?
Serial port are instantiated from a class already, so it's not necessary to derive from them.

For example, from Arduino reference:
"All Arduino boards have at least one serial port (also known as a UART or USART): Serial."
"The Arduino Leonardo board uses Serial1 to communicate via RS232 on pins 0 (RX) and 1 (TX). Serial is reserved for USB CDC communication."

Anyways, if I'm reading this correctly, it looks like the instantiation is done in HardwareSerial.cpp/.h from a class named HardwareSerial into instances Serial, Serial1, and so on. This is included from Arduino.h so, at least you can trust that they are always accessible hardware supporting.. Since the Serial classes are already instantiated in memory, you should just pass the related instances by reference to save memory unless you insist on overriding some functions like read and write. Outside Arduino IDE this probably works in a different way.

However, my point about function pointers was that it kinda doesn't belong in C++ when you have other neater ways of doing the same thing in my opinion. Function pointers are typically used in C when you need to collect actions in an array and pass them into function calls. Nothing stops you from doing the same within a class though. It's a separate topic from above because I don't see what would the passed function be.
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I experimented with your suggestions, wrote a stub for the menu just as a simple test if I can use serial I/O from both, the Menu class *and* the Arduino sketch.

It worked right ahead, and like always afterwards you cannot understand why I didn't get that right first time...
Didn't I try about the same thing as one of my first test?  Well obviously not *exactly* the same smiley-wink

Code:
/*
  stub.ino
  Confused about passing I/O functions from the sketch to the menu class
  I wrote this TestStub class to make some very basic I/O tests.
  Based on insights and suggestions from a discussion in Arduino forums.
  Special thanks to Nick Gammon, Chaul, johncc.
*/

/* **************************************************************** */
/* Library: */

class TestStub {
public:
 TestStub(Stream & port, int (*maybeInput)(void))
   : port_ (port),
    maybe_input(maybeInput)
  { }

  // Basic tests for menu output:
  void out(const char *str) {
    port_.print(str);
  }

  // Testing overloading out():
  void out(const char c) { port_.print(c); }
  void out(const int i)  { port_.print(i); }

  // Some basic output functions:
  void ln() { port_.print('\n'); }
  void tab() { port_.print('\t'); }

  bool lurk() {
    /* int maybe_input() 
       must be a function returning one byte data
       or if there is no input returning EOF */
    int INP=(*maybe_input)();

    if ( INP != EOF ) { // there *is* input
      out("Lurker sees input:\t");
      out((char) INP);        // I don't care about newline here ;)
      tab();
      out(INP);
      ln();

      return true;
    }
    return false;
  }

private:
  Stream & port_;
  int (*maybe_input)(void); // maybe_input()  Must return EOF or next char
};


/* **************************************************************** */
/* Arduino program: */

int men_input() { // men_input()    Must return EOF or next char
  if (!Serial.available())
    return EOF;

  return Serial.read();
}

TestStub STUB(Serial, &men_input);

void setup() {
  Serial.begin(115200);
  STUB.out("Basic c++ I/O test...\tTesting maybe_input(), string out(), lurk():\n");
}

long cnt=0;

void loop() {
  STUB.lurk(); // lurks for input, then trolls.

  // Test if I can still call Serial.print() from main():
  if (! (cnt++ % 100000L)) {
    Serial.print("Serial.print() from main().\t");
    Serial.println(cnt -1);
  }
}

/* **************************************************************** */

Changing existing git code to this implementation should be straight ahead. I hope to do that today.
Thanks a lot for all the input.
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, yes changing the existing git code to this implementation was quick, but...
it gave the same re-definition problems as before smiley-wink

After a lot of trying this&that turns out that it (probably) was some #include logic,
like not having loaded Arduino.h in a file when I thought I had,
resulting in names changing meaning while compiling or similar.

Starts working now on Arduino.

I still have some details to chew on:

* PROGMEM strings
  I have used them so far for constant strings in the user interface.
  Things would get easier if I could drop them (without using RAM)

  I have read that they could be replaced by string constants. Searching a bit does not sound so simple.
  If I *only* use them for string output functions it seems a save bet that the compiler would optimize them away.
  Is that true?

* How can I use (test) the code on the PC? (Linux, in my case).
  Still not really understanding that stream stuff...
  What do I #include that it will know 'Stream' on the PC, and what about 'Serial'?
Logged

Global Moderator
Melbourne, Australia
Offline Offline
Shannon Member
*****
Karma: 226
Posts: 14101
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

To use program memory for strings, just use the F macro, as in this modified example:

Code:
class myMenu
  {
  private:
    Stream & port_;
  public:
    myMenu (Stream & port) : port_ (port) { }
    void begin ();
  };

void myMenu::begin ()
  {
  port_.println (F("Menu initialized."));    // <-------- keep string in PROGMEM
  }

myMenu menu (Serial);

void setup ()
  {
  Serial.begin (115200);
  menu.begin ();
  }  // end of setup

void loop () { }

As for testing on the PC, I personally just upload to my board each time. Trying to do stuff on the PC is going to mean getting the libraries included etc.
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@Nick Thank you for all your help.

I'd prefer keeping the possibility to compile test code for the PC for a while, if it does not prove to be too difficult.
I plan to do some stuff where I will need fast edit-compile-test cycles to find my way through smiley-wink
Still feeling very new to that c++ stuff.

I have found a way to have both now, doing an ugly hack called preprocessor magic.
I think I will drop that once feeling more comfortable with c++ after doing some more coding.
You are right: it *is* a hassle to maintain...

Not decided on PROGMEM yet, will do some more tests.
F("string") macro is fine for Arduino, but another thing to be taken care of as long as I want to use a PC as test platform.

My current version is now online, comments welcome.
https://github.com/reppr/pulses
« Last Edit: February 25, 2013, 09:39:49 am by OutOfLine » Logged

Global Moderator
Melbourne, Australia
Offline Offline
Shannon Member
*****
Karma: 226
Posts: 14101
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

F("string") macro is fine for Arduino, but another thing to be taken care of as long as I want to use a PC as test platform.

Hardly a lot of trouble though, just get the macro to return the original string.
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hardly a lot of trouble though, just get the macro to return the original string.

True.

For strings used *multiple times* I think declaring them as a named constant used only to pass as const parameter to const output functions should produce very similar results as declaring them as PROGMEM.

I'm not definitely sure about that yet, though.

Logged

Global Moderator
Melbourne, Australia
Offline Offline
Shannon Member
*****
Karma: 226
Posts: 14101
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The compiler will do that anyway, I believe. Example:

Code:
const char * foo = "But does it get goat's blood out?";
const char * bar = "But does it get goat's blood out?";

void setup ()
  {
  Serial.begin (115200);
  Serial.println (foo);
  Serial.println (bar);
  Serial.println ((int) foo);
  Serial.println ((int) bar);
  }  // end of setup

void loop () { }

The second pair of lines shows that foo and bar are actually the same string:

Code:
But does it get goat's blood out?
But does it get goat's blood out?
256
256

Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Interesting, and a nice trick to test that.

I have started some testing too,
testing RAM usage and sketch size influence of Arduino F() macro:

*Not* using F():                                      Using Arduino F() macro:

  Binary sketch size: 7,206 bytes             Binary sketch size: 7,534 bytes
  RAM: 465                                              RAM: 1525

nice smiley
Logged

Global Moderator
Melbourne, Australia
Offline Offline
Shannon Member
*****
Karma: 226
Posts: 14101
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Proof?
Logged


Offline Offline
Jr. Member
**
Karma: 1
Posts: 65
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Continued testing...

Testing RAM usage and sketch size:

Using Arduino F() macro *only*,
eliminating const strings and all PROGMEM stuff:

Binary sketch size: 7,534 bytes
RAM: 1685

even better smiley

Logged

Pages: 1 [2] 3   Go Up
Print
 
Jump to: