It appears that I don't understand #ifdef--"multiple definition error"

It seems that I don't correctly understand #ifdef #elif . . . (OK, or maybe the compiler is out to get me, but . . .))

If I include huh.h in an otherwise empty sketch (before setup()), it compiles without a problem:

#ifndef dhMegaLib
#define dhMegaLib

#include "Arduino.h"



class nullSerial {
    void begin(void);
    void println(void);
    void print(void);
    int available();
    void read(void);
    void write(void);
    void end(void);
};



// now a device to send monitor messages
#if dbgTyp==HARDWARE
HardwareSerial &monSerial = Serial;
#elif dbgTyp==ALTSOFTSERIAL
AltSoftSerial monSerial;
#elif dbgTyp==SOFTSERIAL
SoftwareSerial monSerial;
#else
dbgTyp==NONE
//there is no monitor, so make an empty class for it
//   all the calls shoudl get optimized out by the compiler
nullSerial monSerial;
#endif  //monitor hardware #if


#endif

It has four alternative declarations of monSerial, depending upon what is or isn't defined.

Hmm, and on further review, why doesn't the line "dbgTyp=NONE" throw an error?

But if I use #ifdef/#elif to do the same thing, it throws an error for multiple declarations:

#ifndef dhMegaLib
#define dhMegaLib

#include "Arduino.h"

#include "SoftwareSerial.h"
#include "AltSoftSerial.h"


// some types to use

enum srlTyps {
  NONE, HARDWARE, SOFTSERIAL, ALTSOFTSERIAL
};

//possible status for the bt device
enum btStatStates {
  BTOFF, BTON, BTPOWERED, BTPENDING, BTAT
};

//an empty class that will get optimized out if used.

//this lets us keep our debugging code in the sketch

class nullSerial {
    void begin(void);
    void println(void);
    void print(void);
    int available();
    void read(void);
    void write(void);
    void end(void);
};


// various serial possibilities

//figure out where bt goes

#ifdef useAltSoftSerialBt
altSoftSerial btSerial;
#elif defined(useSoftwareSerialBt)
SoftwareSerial btSerial;
#elif defined(useHardwareSerialBt)
HardwareSerial &btSerial=Serial;
#else
nullSerial btSerial;
#endif //btSerial ifdef


#endif

What I'm trying to accomplish is a library that handles my commonly used things, and the ability to reroute the debugging and bluetooth serial output by #define in my main code program (none were used here; it is a blank program with #include "huh.h" or "dhMegaLib.h"

My (probably deeply flawed) understanding is that the preprocessor should strip out every line except for the "successful" condition, so that there is only one declaration of btSerial. But the compiler doesn't seem to agree . . .

No expert here but I don’t believe equals signs in preprocessor directives is legal:

#if dbgTyp==HARDWARE
HardwareSerial &monSerial = Serial;
#elif dbgTyp==ALTSOFTSERIAL
AltSoftSerial monSerial;

I would try something like:

#ifdef HARDWARE
HardwareSerial &monSerial = Serial;
#endif
#ifdef ALTSOFTSERIAL
AltSsoftSerial monSerial;
#ifdef , etc.
enum srlTyps {

NONE, HARDWARE, SOFTSERIAL, ALTSOFTSERIAL
};

The preprocessor does not understand enums (which are, after all, a higher level C construct.)
If you want to check symbolic values with #if, then they'll need to be defined with "#define" instead.

#define NONE 0
#define HARDWARE 1
#define SOFTSERIAL 2
#define ALTSOFTSERIAL 3
  :
#define dbgType HARDWARE
  :
#if (debgType == HARDWARE) ...
};

dougp:
No expert here but I don't believe equals signs in preprocessor directives is legal:

...
I would try something like:

#ifdef HARDWARE

HardwareSerial &monSerial = Serial;
#endif
...

Now you've really got me scratching my head . . .

My first block with enum and equality tests is the one that compiles.

It's the second with the #ifdef/elif block that fails.

Aren't my #ifdef blocks exactly equivalent to your multiple #ifdef?

dochawk:
Aren't my #ifdef blocks exactly equivalent to your multiple #ifdef?

Can't say for certain. I'd follow @westfw's guidance, he's much more knowledgeable about these things.

I think I've found the issue.

dhMegaLib.cpp needs to explicitly include dhMegaLib.h.

In dhMegaLib.h, I'm declaring variables--which shouldn't be happening for the header file for a library.

I think that if I'm going to used a standardized set of variable declarations, it needs to go in a separate include file.

The offending code was there as I'll want identical switches in various projects. [to use a software serial while debugging so that I can use the monitor in the IDE, and then switch to using the UART for the bluetooth, and a nullSerial for the monitor when it's deployed]

Is there a standard/normal/recommended way to implement the common file?

"declare" variables in a .h file, but "define" them (initialize) in the most relevant .c file:

// foo.h
extern uint32_t foo_config1;
extern uint8_t foo_buffer[80];
//foo_top.c
uint32_t foo_config1 = 0;
uint8_t foo_buffer[80];

"extern" should work like a forward declaration if the variable is actually defined later in the file.

Ahh. I see.

I'm learning more c and c++ then I ever meant to . . . :slight_smile:

Is a "monSetup.c" file that will get included a safe practice?

And some day soon, first thing in the morning before my head fills or I use up my daily neuron application, I'll try to digest the templates . . .

No, don't #include .c files. Not ever!

General principle - .h files should never create either code or variables; they should contain only "declarations."

You might be able to do something like:

Stream *monser;
void setup() {
   static SoftwareSerial myser(...);
   myser.begin(...);
   while (!myser)
       ;
   monser = &myser;
{
void loop() {
   monser->println("I made it into loop");
   :
}

What I'm really after here is a chunk of code/declarations/whatever that is shared between multiple programs, so that I can change it once and have it apply to all future projects (or to revisiting and updating of prior projects).

The current blocks work, but would have to be changed in every program.