Baffling problem defining an array in a header file

Hi all,

I have this H file for an Arduino library:

#ifndef STD_IN_OUT_H
#define STD_IN_OUT_H

#include <Stream.h>

class STDINOUT
{
    private:
        #define PTR_COUNT 4 // file & stream how many pointers?
        #define STDIN     0 // standard input
        #define STDOUT    1 // standard output
        #define STDERR    2 // standard error
        #define DUMMY     3 // null to signify no match
        uint8_t _x; // generic loop counter

        // file pointers
//      static FILE *_file_ptr[] = { NULL, NULL, NULL, NULL, };
        static FILE *_file_ptr[PTR_COUNT];

        // stream pointers
//      static Stream *_stream_ptr[] = { NULL, NULL, NULL, NULL, };
        static Stream *_stream_ptr[PTR_COUNT];

        static int _getchar0 (FILE *); // char read for stdin
        static int _putchar1 (char, FILE *); // char write for stdout
        static int _putchar2 (char, FILE *); // char write for stderr
    public:
        void open (Stream &); // open all as one
        void open (Stream &, Stream &); // open in as one, both as out
        void open (Stream &, Stream &, Stream &); // specify each one
        void close (void); // close streams & release memory
        Stream &getStream (FILE *); // get current stream to restore if necessary
};

extern STDINOUT STDIO; // Expose STDIO object

#endif // #ifndef STD_IN_OUT_H

See the lines pointed to by “<------ THIS”? Well, all they are is this:

[b]
static FILE *_file_ptr[4];
static Stream *_stream_ptr[4];[/b]

Now, I’ve declared arrays in H files before with no problems, but now I get this error (lots of compiler output lines snipped out):

/tmp/build6365634550864386270.tmp/core.a(Stdinout.cpp.o): In function `STDINOUT::_getchar0(__file*)':
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/tmp/build6365634550864386270.tmp/core.a(Stdinout.cpp.o): In function `STDINOUT::_putchar1(char, __file*)':
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/tmp/build6365634550864386270.tmp/core.a(Stdinout.cpp.o):/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: more undefined references to `STDINOUT::_stream_ptr' follow
/tmp/build6365634550864386270.tmp/core.a(Stdinout.cpp.o): In function `STDINOUT::close()':
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/tmp/build6365634550864386270.tmp/core.a(Stdinout.cpp.o): In function `STDINOUT::open(Stream&, Stream&, Stream&)':
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:34: undefined reference to `STDINOUT::_stream_ptr'
/tmp/build6365634550864386270.tmp/core.a(Stdinout.cpp.o): In function `STDINOUT::getStream(__file*)':
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:88: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:88: undefined reference to `STDINOUT::_file_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:89: undefined reference to `STDINOUT::_stream_ptr'
/usr/share/arduino-1.0.6/libraries/Stdinout.cpp:89: undefined reference to `STDINOUT::_stream_ptr'
collect2: error: ld returned 1 exit status

Here is some CPP code that these errors reference:


** **31 // connect stdin to input device, stdout and stderr to output device 32 void STDINOUT::open (Stream &inpstr, Stream &outstr) 33 { [color=red]34     open (inpstr, outstr, outstr);[/color] 35 }** **


** **84 // return stream connected to FILE (i.e. stdin, stdout or stderr). 85 Stream &STDINOUT::getStream (FILE *fp) 86 { 87     for (_x = 0; _x < PTR_COUNT; _x++) { // scan through them [color=red]88         if (_file_ptr[_x] == fp) { // if stdio matches...[/color] [color=red]89            return *_stream_ptr[_x]; // ...return it's pointer[/color] 90         } 91     } 92 }** **


If I change the two lines (see above what is commented out), I get this error instead (again, excess listing stripped):

[tt]/usr/share/arduino-1.0.6/libraries/Stdinout.h:40:30: error: a brace-enclosed initializer is not allowed here before '{' token
   static FILE *_file_ptr[] = { NULL, NULL, NULL, NULL, };
                              ^
/usr/share/arduino-1.0.6/libraries/Stdinout.h:40:56: error: invalid in-class initialization of static data member of non-integral type 'FILE* [] {aka __file* []}'
   static FILE *_file_ptr[] = { NULL, NULL, NULL, NULL, };
                                                        ^
/usr/share/arduino-1.0.6/libraries/Stdinout.h:44:34: error: a brace-enclosed initializer is not allowed here before '{' token
   static Stream *_stream_ptr[] = { NULL, NULL, NULL, NULL, };
                                  ^
/usr/share/arduino-1.0.6/libraries/Stdinout.h:44:60: error: invalid in-class initialization of static data member of non-integral type 'Stream* []'
   static Stream *_stream_ptr[] = { NULL, NULL, NULL, NULL, };
                                                            ^

[/tt]

…and the code it references (in Stdinout.h)

[b]39         // file pointers
[color=red]40         static FILE *_file_ptr[] = { NULL, NULL, NULL, NULL, };[/color]
41 //        static FILE *_file_ptr[PTR_COUNT];
[/b]

Please see next post for the complete source listing if you need it, and thanks in advance for any help!

(edit): Forgot to mention something very important: IF I declare those two arrays OUTSIDE the class (or in the CPP file) then it all works. What the heck???

File listings:

(Stdinout.cpp)

//////////////////////////////////////////////////////////////////////////////
//
//  Stdinout.cpp - connect various character devices to standard streams for Arduino
//  Copyright (c) 2014, 2018 Roger A. Krupski <rakrupski@verizon.net>
//
//  Last update: 04 February 2018
//
//  This library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////

#include <Stdinout.h>

// connect stdin, stdout and stderr to same device
void STDINOUT::open (Stream &iostr)
{
    open (iostr, iostr, iostr);
}

// connect stdin to input device, stdout and stderr to output device
void STDINOUT::open (Stream &inpstr, Stream &outstr)
{
    open (inpstr, outstr, outstr);
}

// connect each stream to it's own device
void STDINOUT::open (Stream &inpstr, Stream &outstr, Stream &errstr)
{
    close();  // close any that may be open

    _file_ptr[STDIN] = fdevopen (NULL, _getchar0);
    _stream_ptr[STDIN] = &inpstr;

    _file_ptr[STDOUT] = fdevopen (_putchar1, NULL);
    _stream_ptr[STDOUT] = &outstr;

    _file_ptr[STDERR] = fdevopen (_putchar2, NULL);
    _stream_ptr[STDERR] = &errstr;

    _file_ptr[DUMMY] = NULL;
    _stream_ptr[DUMMY] = NULL;
}

// disconnect stdio from stream(s)
void STDINOUT::close (void)
{
    for (_x = 0; _x < PTR_COUNT; _x++) { // gotta do 'em all!
        fclose (_file_ptr[_x]);
        _file_ptr[_x] = NULL;
        _stream_ptr[_x] = NULL;
    }
}

// read a char from stdin
int STDINOUT::_getchar0 (FILE *fp)
{
    while (! (_stream_ptr[STDIN]->available())); // yeah it blocks - shoot me.
    return (_stream_ptr[STDIN]->read());
}

// write a char to stdout
int STDINOUT::_putchar1 (char c, FILE *fp)
{
    return _stream_ptr[STDOUT]->print ((char) c);
}

// write a char to stderr
int STDINOUT::_putchar2 (char c, FILE *fp)
{
    return _stream_ptr[STDERR]->print ((char) c);
}

// return stream connected to FILE (i.e. stdin, stdout or stderr).
Stream &STDINOUT::getStream (FILE *fp)
{
    for (_x = 0; _x < PTR_COUNT; _x++) { // scan through them
        if (_file_ptr[_x] == fp) { // if stdio matches...
            return *_stream_ptr[_x]; // ...return it's pointer
        }
    }
}

STDINOUT STDIO; // Preinstantiate STDIO object

// end of stdinout.cpp

(Stdinout.h)

//////////////////////////////////////////////////////////////////////////////
//
//  Stdinout.h - connect various character devices to standard streams for Arduino
//  Copyright (c) 2014, 2018 Roger A. Krupski <rakrupski@verizon.net>
//
//  Last update: 04 February 2018
//
//  This library is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////

#ifndef STD_IN_OUT_H
#define STD_IN_OUT_H
#error NOT WORKING YET
#include <Stream.h>


class STDINOUT
{
    private:
        #define PTR_COUNT 4
        #define STDIN     0
        #define STDOUT    1
        #define STDERR    2
        #define DUMMY     3
        uint8_t _x; // generic

        // file pointers
        static FILE *_file_ptr[] = { NULL, NULL, NULL, NULL, };
//        static FILE *_file_ptr[PTR_COUNT];

        // stream pointers
        static Stream *_stream_ptr[] = { NULL, NULL, NULL, NULL, };
//        static Stream *_stream_ptr[PTR_COUNT];

        static int _getchar0 (FILE *); // char read for stdin
        static int _putchar1 (char, FILE *); // char write for stdout
        static int _putchar2 (char, FILE *); // char write for stderr
    public:
        void open (Stream &);
        void open (Stream &, Stream &);
        void open (Stream &, Stream &, Stream &);
        void close (void);
        Stream &getStream (FILE *);
};

extern STDINOUT STDIO; // Expose STDIO object

#endif // #ifndef STD_IN_OUT_H

// end of Stdinout.h

Please try to add “#include <stdio.h>” to your header.

Sorry, I jumped the gun.. Your declaration is wrong since a static declaration must be done in two steps (or it must be inlined)..

header (dummy_obj.h):

class dummy {
  static int foo; //No initialization allowed
  int getFoo();
};

implementation (dummy_obj.cpp):

#include "dummy_obj.h"
int dummy::foo = 0; //Initialization required

int dummy::getFoo() {
  return dummy::foo;
}