Go Down

Topic: How to pass an I/O device "object" to a function. (Read 379 times) previous topic - next topic

Krupski

Hi all,

Here's what I am trying to do and can't seem to figure it out:

Say I have a character device like a graphics display. To use it, I include it's driver library and then make an instance of the "object" that is a "handle" for the device (sorry if the terminology is wrong).

For example:

Code: [Select]
#include <Noritake_VFD_GUU100.h>
static Noritake_VFD_GUU100 VFD;


Now I can do things like VFD.print ("Hello");  or VFD.setCursor (0, 0); etc......

What I want to be able to do is PASS "VFD" itself to a function, for example like this:

Code: [Select]

// the function
void my_function (?pointer, char *string)
{
        pointer.print (string);
}

// to use it
char *message = "Hello there";
my_function (VFD, message);


The reason I need to figure out how to do this is that I'm wanting to write a sort of "fopen/fclose" type of library where I can open and close "file pointers" to different devices by using the AVR FDEV_SETUP_STREAM() functions. Ultimately, I want to do something like this (imagine I call my function "devopen"):

Code: [Select]

FILE *disp = devopen (VFD, "wb"); // make a pointer to the graphics display
FILE *ser = devopen (Serial, "wb+"); // make a file pointer for serial i/o
fprintf (disp, "Hello there"); // print to the graphics display
fprintf (ser, "Printing to serial"); // send text to the serial port
..... blah blah ....
devclose (disp); // free disp == NULL now
devclose (ser); // free the serial pointer == NULL (note "Serial" from "Serial.begin" is still active!)



My basic problem is I can't seem to figure out what KIND of pointer to cast for the device object.

Any help will be appreciated (or if this has already been done, let me know!)

Thanks!

-- Roger
Gentlemen may prefer Blondes, but Real Men prefer Redheads!

pYro_65

#1
Dec 14, 2013, 12:39 am Last Edit: Dec 14, 2013, 12:56 am by pYro_65 Reason: 1
You could use a pointer or reference type for this:

Code: [Select]

void my_function ( Noritake_VFD_GUU100 *pointer, char *string)
 {
       pointer->print( string );
 }


Or

Code: [Select]

void my_function ( Noritake_VFD_GUU100 &ref, char *string)
 {
       ref.print(string);
 }


However if it is only the print functionality you want ( and it supports the builtin Print library )
Code: [Select]
void my_function ( Print &printObj, char *string)
 {
       printObj.print(string);
 }


This method can accept any print/stream object ( Serial, EthernetClient, Wire,... )

Any of these objects can be cast to an intermediate reference or pointer also:
Code: [Select]

Print *p = ( Print* ) &Serial;

p->println( "Over Serial" );

p = ( Print* ) &VFD;

p->println( "Over VFD" );


Here is a solution for writing to multiple print targets, maybe it can be modified to suit your needs:
http://forum.arduino.cc/index.php?topic=200975.msg1481633#msg1481633

Krupski


You could use a pointer or reference type for this:
======== snip ========
These are only really useful if your variable isn't global. If it is, you waste extra stack passing around something that the code can already see.


In the first code block, what does "Noritake_VFD_GUU100 *pointer" mean? Is it casting "pointer" as type "Noritake_VFD_GUU100"?

If so, that won't work because Noritake..... may not exist. This needs to be generic.

As far as functionality, I won't even be using Print. As I said in the OP, what I ultimately want to do is pass the pointer to FDEV_SETUP_STREAM() and create a standard "FILE *" data type to use with fprintf, fread, fwrite, etc...

For example, if I try to do this:

[font=monospace]fprintf (VFD, "Hello");[/font]

of course it won't compile and this is the error message:

[font=monospace]
test.ino: In function 'void setup()':
test.ino:50:23: error: cannot convert 'Noritake_VFD_GUU100' to '__file*' for argument '1' to 'int fprintf(__file*, const char*, ...)'
[/font]

My problem is I don't know what "type" 'Noritake_VFD_GUU100' is.

As you can see in the error, fprintf takes type "__file*", a const char *, other stuff and returns an "int".

But there is no type data for "Noritake_VFD_GUU100" and THAT'S what I need to know.
Gentlemen may prefer Blondes, but Real Men prefer Redheads!

Nick Gammon

How about this?

Code: [Select]

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

void myMenu::begin ()
  {
  port_.println ("Menu initialized.");
  }

myMenu menu (Serial);

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

void loop () { }


In that example I passed a reference to a class derived from Stream (which includes Serial, Serial1, etc.) to a class constructor.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

pYro_65

#4
Dec 14, 2013, 01:23 am Last Edit: Dec 14, 2013, 01:25 am by pYro_65 Reason: 1
That has just wrapped the object in another object, might as well just assign to a pointer or reference:

Code: [Select]

Stream *S = &Serial;
Stream &s = Serial;


Pointers are also required:
Quote
If so, that won't work because Noritake..... may not exist. This needs to be generic.




majenko

What you're really looking for is what's known as "inheritance".

In the C++ world classes come in two basic flavours - stand-alone or inherited.  A stand-alone class is your basic class:

Code: [Select]

class myClass {
  public:
   void begin();
};


Which is all well and good.

Then there's inherited classes.  These take another class as a "base" class and add to them with extra functions.  For example:
Code: [Select]

class myInheritedClass : public myClass {
  public:
    void doSomething();
};


The myInheritedClass inherits all the functions from myClass, so there is a .begin() function in there as well as the doSomething() function.  Not only that, but it also inherits the type of it's base class - and ultimately the type of any base classes to that class.  So an object of class myInheritedClass can also be referred to as an object of class myClass - but doing that it'll only know about the .begin() function.  This facility is called "polymorphism" and is where much of the power of classes comes in to play.

So you can have lots of different classes for different (yet similar) things, like display modules, which all inherit, say, the Print class so you can use all the .print() / .println() functions with them.  So you could have:
Code: [Select]

class fooDisplay : public Print {
  public:
    int write(int c); // function to write to the display
};

class barDisplay : public Print {
  public:
    int write(int c); // different function to write to the other type of display
};

fooDisplay foo;
barDisplay bar;

void printSomething(Print *ptr, char *text) {
  ptr->print(text);
}

//...
printSomething(&foo, "This is on foo");
printSomething(&bar, "This would be on bar");

Two different classes, but both with the same pedigree.  It's like having a collie and a red setter.  They're markedly different dogs, but they are both dogs, and  things that can be done to all dogs can be done to either kind of dog.  But you can't round up sheep with a red setter.

pYro_65


As far as functionality, I won't even be using Print. As I said in the OP, what I ultimately want to do is pass the pointer to FDEV_SETUP_STREAM() and create a standard "FILE *" data type to use with fprintf, fread, fwrite, etc...


You might as well just use print, the writing functions fprintf and fwrite use will have to call print, so you can avoid the overhead by removing the entire file handling ( sd libs support print as well. ), majenko explains well what my DualWriter class is using.

Krupski


How about this?

Code: [Select]

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

void myMenu::begin ()
  {
  port_.println ("Menu initialized.");
  }

myMenu menu (Serial);

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

void loop () { }


In that example I passed a reference to a class derived from Stream (which includes Serial, Serial1, etc.) to a class constructor.


THIS looks like what I'm after.... but I'll have to try it to see if it works for my case... and I have to run to mom-in-law's house for dinner, so it has to wait (darn!).

Thanks... I'll try it and post what happens.
Gentlemen may prefer Blondes, but Real Men prefer Redheads!

Krupski


That has just wrapped the object in another object, might as well just assign to a pointer or reference:

Code: [Select]

Stream *S = &Serial;
Stream &s = Serial;


Pointers are also required:
Quote
If so, that won't work because Noritake..... may not exist. This needs to be generic.


THAT'S IT!!!!!!!!!!!! It works and it's exactly what I was looking for. Just so you understand what I was trying to do, look at this code:

Code: [Select]
void setup (void)
{
        Serial.begin (115200);
        testIt (Serial);
}

void loop (void)
{
}

void testIt (Stream &ptr)
{
        ptr.print ("Hello");
}


Now that I can pass a "print device" to a function, I can now write my "fopen" and "fclose" code to create standard FILE* pointers to use with "fprintf", "fwrite", etc....

Thanks so much!

-- Roger



Gentlemen may prefer Blondes, but Real Men prefer Redheads!

Go Up