chipKIT® Development Platform

Inspired by Arduino™

re#define before or after #include

Created Sun, 06 Mar 2016 01:51:52 +0000 by FredCailloux


FredCailloux

Sun, 06 Mar 2016 01:51:52 +0000

Here is the pertinent part of my sketch for which I have difficulties figuring out how to implement redefinition of a #define line. I am building a Class for my LS7366 chip to run with the Digilent DSPI library. The Class SPI7366 is built so that it is using the DSPI0 object by default. At least that's what I am trying to implement. So I use the lines:

#ifndef SPIPORT  // in the header file SPI7366.h
#define SPIPORT DSPI0 
#endif

in my header file, hoping that the preprocessor will define the word SPIPORT back to DSPI0 when the file gets included. If, in further projects I want to use the DSPI2 object, all I would have to do is insert a line in my sketch to redefine the SPIPORT to DSPI2 as in:

#define SPIPORT DSPI2  // in a further sketch

And it doesn't work. ;) For some reason that I cant figure out the preprocessor is either choking with an error or just define my object name to some strange thing that render the Class inoperable. In an attempt to debug the situation I forced the Class header to define the object name LScom to DSPI2 as in:

DSPI2 LScom;   // forced definition of SPI object named LScom in the SPI7366.h header file

And it started working like water flowing in a river. Here is a more complete (but partial) pertinent part of the code:

// Implementation Sketch		
#include <SPI7366.h> /* 		
#define SPIPORT DSPI2		
SPI7366	LSspi;	
void setup() {	LSspi.initSPI();	
	LSspi.enableCount( ) ; }	
void loop() {	LSspi.read1Count( );	
	myCount = LSspi.getCount( ) ;	
	Serial.println (myCount) ;	
	delay(1000) ; }	
		
#ifndef _SPI7366_H // // header file SPI7366.h
	#define _SPI7366_H	
	#include <stdint.h>	
	#include <DSPI.h>	
	#ifndef SPIPORT	
	#define SPIPORT DSPI0 	
	#endif	
		
	class SPI7366 {	
	public:	
	SPI7366();	// Class SPI7366 constructor
	SPIPORT LScom;	// define SPI object named LScom
	};	
#endif		
		
		
#include <SPI7366.h>	// SPI7366.cpp file	
SPI7366::SPI7366( ) { }		
		
void SPI7366::begin() {  }		
		
void SPI7366::initSPI(){		
LScomm.begin() ;		
LScomm.setSpeed(250000);		
delay(10);		
Serial.println ("init:");}

I tried putting the #define SPIPORT DSPI2 before the #include SPI7366.h as a first line but it doesn't work either. Of course it could not work because when the #define SPIPORT DSPI2 line is executed the preprocessor doesn't have the pertinent code to apply the #definition cause the SPI7366.h file is not loaded already. So it's kind of a head to tail problem. There's got to be a solution to this, it's just that it's beyond my skill so far.

So, here is the question: How can I implement this preprocessor behavior so that I can redefine the SPIPORT word to something else when it as already been defined ?


majenko

Sun, 06 Mar 2016 11:14:04 +0000

You can't.

When you #define something it is only #defined in the file that you #define it in, and in any files that are #included within that file. It's not #defined in any other files, nor in any files included in any other files. That includes the .cpp file.

Compilation is made up of a number of compilation units where each is completely separate. At the end of compilation they are linked together to form the resultant hex file.

Simply put, you get (amongst other things):

  • Sketch INO + Header = Sketch.o
  • Library CPP + Header = Library.o
  • Sketch.o + Library.o = HEX file

#defining something in the Sketch INO doesn't define it in the Library CPP because it's not defined in it nor in any file that is #included by it.

There are two basic ways around the problem - the hard, cryptic, but efficient way, and the easy, understandable, though maybe less efficient way. I always use the latter since it is so much easier to know what is going on.

The first way is to use C++ templates to provide the DSPI port as part of the constructor:

LSspi<DSPI0> myLS;

If you're curious about that method you can read up about C++ templates, but they're not for the faint of heart (also it means all your code ends up in the header file which is something I don't like, even if it is templated).

The second way is to pass a pointer to a DSPI object as a parameter to the constructor and then store that pointer in your class. You then use that pointer for all your SPI calls. It's the method I always use:

class foo {
    private:
        DSPI *_spi;
    public:
        foo(DSPI &spi) : _spi(&spi) {}
        foo(DSPI *spi) : _spi(spi) {}
        void begin() {
            _spi->begin();
        }
};

DSPI2 spi;
foo MyFoo(spi);

Note the double constructor there - that allows you to use either a reference or a pointer - so you can either use "&spi" in the constructor or just "spi" and it doesn't matter which - means the end user doesn't have to remember if it's a pointer or not that he needs to pass.

For some systems I also create special profile classes if you want to use things in specific sockets. For instance, for the OLED PMOD I have a set of special wrapper classes like this:

class SSD1306_PMOD_D : public SSD1306 {
    public:
        SSD1306_PMOD_D() : SSD1306(new DSPI0(), 24, 28, 31, 30, 29) {}
};

It just creates a new class that inherits the main class with default parameters - note that because of the double constructor above you can use "new DSPI0()" in the constructor - since it creates a pointer. Using that class is then as simple as:

SSD1306_PMOD_D oled;

FredCailloux

Mon, 07 Mar 2016 00:30:16 +0000

All this is a bit beyond my expertise, hence, I will have to do some digestive thinking here (and study). And then, I'll most probably come back to you with some more questions... Anyhow, teaching very appreciated. I will rethink my class, this approach appear to be the most elegant and functional way to go. Thanks


FredCailloux

Tue, 08 Mar 2016 02:59:19 +0000

Hello Majenko, In my bit of code, on the original post, I list the sketch, the header and the cpp files (at least part of them). As listed in this example, the cpp file is related to the header only with the DSPI object called LSspi (which by default is suppose to take DSPI0 definition). If I can manage to re-edit the header file only, I will have what I want, ie: changing the DSPI object from DSPI0 to DSPI2 or DSPI1. Nowhere is this strategy I attempted to modify the cpp file. So, please let me rephrase my question with better accuracy if I may: How can I implement this preprocessor behavior so that I can redefine the SPIPORT word in the header file to something else when it has already been defined in the header ? OR, Is there a way to be able to change the word SPIPORT to something else (DSPI2 or DSPI1) within the header file, whether I do it before or after the #include SPI7366 header or by another strategy ? I was thinking to maybe use conditional #define directive to check one defined variable in the sketch that would command the preprocessor to #define SPIPORT based on a choice, a little bit like a Switch Case clause kind of preprocessor command, is there such a thing ? Does that make sense ? Thanks for your inputs


majenko

Tue, 08 Mar 2016 08:54:53 +0000

What you have to remember is that a header file isn't an individual entity in its own right. When you #include the file it literally replaces the #include statement with the content of the file. That means it is replaced direct into the cpp file in one instance, and also replaced into your sketch in another instance. So you have two copies of it, and the two are completely separate.

So doing any kind of definition only affects the file that it is defined in - your sketch in this case.

It's like if you have two pieces of paper and write "hello = goodbye. Hello" on one and just "hello" on the other. Only one of them knows that hello = goodbye - on the other hello is just hello.

The only possible way you can do it with a #define is if that #define is in a file that is #included by the header file, and that gets a bit pointless, since the user may as well just edit the header file itself.

The only other "global" place where you can define something is on the command line with -D, but that limits you to what you can do and where. In MPIDE you can't do it at all. In UECIDE you can define a special control file in the library that gives you menu entries to set -D options. In the Arduino IDE again you can't do it.

Sent from my SM-T555 using Tapatalk


FredCailloux

Tue, 08 Mar 2016 20:00:57 +0000

OK then, I am convinced. I am going to go with your method. So lets get back to your example code here for sake of understanding:

1-	class foo 	{
2-		private:
3-			DSPI *_spi;
4-		public:
5-			foo(DSPI &spi) 	: _spi(&spi) {}
6-			foo(DSPI *spi) 	: _spi( spi) {}
7-					 void begin() 	{
8-						_spi->begin();
9-                                }
10-               };
11-
12-   DSPI2 spi;
13-   foo MyFoo(spi);

I did some reading on templates and classes and, of course, all kinds of questions are popping out :? So, i went on an tried to analyze as much as I could and here is my understanding. I would appreciate if you could read and let me know if I have it or if I am going the wrong track here:

From line 1 to 10 would be lines of code part of the header file ( or would they appear in the cpp file) ? Lines 12 and 13 would be lines of code part of the INO sketch

Line 12 instantiate an object of type class DSPI2 which is inherited from the class DSPI, named spi Line 13 instantiate an object of type foo, using the object spi as a constructor parameter, this object is named MyFoo Line 3 when instantiated, the class object spi will create a variable of type pointer to an object DSPI, this pointer is named _spi. Line 3 this is not the same object spi then the one instantiated at line 12, it is distinct has it is named _spi. Line 5 list the class constructor code, using an intrinsic variable of type pointer to DSPI address, named spi. :?: Line 5 this DSPI address pointer is utilized give value to the private _spi pointer (here I have a question ) :?: Line 6 is another class constructor using a variable of type pointer to DSPI content, named spi (same as line 5) :?: Line 5 and 6 are both constructors. Implementing the Overloading capabilities of classes in C++. Line 5 and 6 Giving capability to the class foo to accept either a pointer to address of the object DSPI or pointer to the content of object DSPI :?: Line 7 is the intrinsic begin() function of any class in C++. It will be executed when the MyFoo object is instanciated. Line 8 is the intrinsic begin() function of object _spi.

Now the questions: Line 5 and 6 are two constructors using a different parameter format. &spi and *spi :?: I have never seen a variable pointer type defined as such ( type &var), such as in (DSPI &spi). Does that mean: variable of type DSPI named spi and refered to by it's address &spi ? Couldn't we just write this line as such (DSPI spi)? I now am confused a little. *spi is the content of the spi object. spi is the object proper. &spi is it not the address of the object spi? My confusion is from line 6 as well. &spi will give a value to *_spi (OK) (*spi) will be used in the construction as (spi) alone ? why not (*spi) and if so, this would generate an error because _spi is a pointer to object DSPI, not spi itself. How come your code would work ? please elaborate in this context.

Thank you for your valuable input. Fred


majenko

Tue, 08 Mar 2016 20:39:16 +0000

LOL... questions galore ;) Ok... here we go:

From line 1 to 10 would be lines of code part of the header file ( or would they appear in the cpp file) ?

Both. I combined them for simplicity. You would split it as you have it now - definition of the class in the header and the code for the class (there is only the begin method there) in a cpp file.

Lines 12 and 13 would be lines of code part of the INO sketch

That is correct.

Line 12 instantiate an object of type class DSPI2 which is inherited from the class DSPI, named spi Line 13 instantiate an object of type foo, using the object spi as a constructor parameter, this object is named MyFoo

Correct.

Line 3 when instantiated, the class object spi will create a variable of type pointer to an object DSPI, this pointer is named _spi. Line 3 this is not the same object spi then the one instantiated at line 12, it is distinct has it is named _spi.

No, _spi and spi are the exact same object. _spi is a pointer to the address of spi.

Line 5 list the class constructor code, using an intrinsic variable of type pointer to DSPI address, named spi. :?: Line 5 this DSPI address pointer is utilized give value to the private _spi pointer (here I have a question ) :?: Line 6 is another class constructor using a variable of type pointer to DSPI content, named spi (same as line 5) :?: Line 5 and 6 are both constructors. Implementing the Overloading capabilities of classes in C++. Line 5 and 6 Giving capability to the class foo to accept either a pointer to address of the object DSPI or pointer to the content of object DSPI :?:

This is where the fun is - a * is a pointer, an & is a reference. They both perform the same job - making 2 variables share the same memory space and thus be the same variable with different names and scopes - but they do it in different ways.

Pointers are a really fun topic and not one that is easy for the novice to get their head around (it took me a number of years, but then I didn't have me for a teacher ;) )

You have a variable, say "spi". You get the address of the variable as a number using "&spi". That address can be assigned to a pointer which is created as "*_spi".

So say the object "spi" has been created and is located in memory at address 69. You create the pointer variable _spi. Let's say that is located at address 123. Into the variable _spi you place the number 69. This is the "pointer" aspect of it in that it "points" to where the "spi" variable is. Because _spi is a pointer and not a real object you have to access it in a special way. In fact there are two ways of accessing it, depending on what you are doing.

In general, to access the members of an object through a pointer you just replace "." with "->", so "spi.begin()" becomes "_spi->begin()". This tells the compiler to "dereference" the pointer and act on the object that it is pointing to.

If it's not an object that is being pointed to but a normal variable, like an "int", then you need to manually dereference the pointer by adding a * to the beginning of it, such as "*intptr = 3", but you can ignore most of that for the time being.

That is all the "classic" way of dealing with pointers - the way that has been in C since the dawn of time. C++ introduced a new way - references.

These perform the exact same job but in a different way - one that can be a little tidier, though more obscured.

References are only ever created when passing a variable to a function as a parameter. You specify a reference as "&varname" in the parameter list, such as

void foo(DSPI &spi) {
    ...
}

That creates a new variable "spi" and places it at the same memory location as the object you pass to the function. This is a real object, but since it shares the same memory location it is the same object as the one you pass. And it has the advantage that you can still access it in the exact same way, so "outsidespi.begin()" stays as "insidespi.begin()" (for example) - the "." stays as a "."

So we have two constructors:

foo(DSPI *spi)    : _spi( spi) {}

That says "Give me a pointer to a DSPI object and I will store that address in my _spi variable.

You would call that as

foo myFoo(&spi)

Then we have:

foo(DSPI &spi)    : _spi(&spi) {}

That says "Give me a DSPI object and I will create a new one at the same address with the same data. I will then get the address of that new object and store it in my _spi variable".

You call it as:

foo myFoo(spi);

So either way the _spi pointer is assigned the address of your "spi" object from your class - either through being manually passed the pointer or through a reference to the object and then getting the address of that reference itself.

It all gets so confusing because of the repeated use for the same symbols (& and *) to mean different things in different places.

You have to just remember:

A * in a variable declaration is a pointer. A * in a variable assignment is a dereference. A & in a function prototype is a "byref" indicator. An & in a variable assignment is a "Get me the address of this variable".

Line 7 is the intrinsic begin() function of any class in C++. It will be executed when the MyFoo object is instanciated.

No, "begin" is an Arduinoism. It's used to get around a problem known as the "Static Initialization Order Fiasco" and is manually called by the user in their sketch.

Line 8 is the intrinsic begin() function of object _spi.

Not intrinsic - see above.

I hope that is as clear as mud :)


FredCailloux

Tue, 08 Mar 2016 21:21:13 +0000

Mud it is, but it gets clearer every minute :D and You are my teacher ;) Now... let me digest for a while... thanks again


FredCailloux

Tue, 08 Mar 2016 21:54:39 +0000

in my INO sketch, instead of two lines such as

DSPI2	spiChoice	            ;
LS7366	MyLS7366 ( spiChoice ) ;

Could I replace with

LS7366	MyLS7366 ( DSPI2 )     ;

What difference would that make, since I will not ever have to address that DSPI object ever in the sketch, I don't need a variable name here, do I ?


majenko

Tue, 08 Mar 2016 22:15:56 +0000

Almost.

There you are referencing the class, not an instance of the class. You need to create a "new" instance of the class:

LS7366   MyLS7366 ( new DSPI2() );

majenko

Tue, 08 Mar 2016 22:16:48 +0000

I generally create a variable because I may well have a number of things on the same SPI bus and want to pass the same object instance through to each constructor - or use it manually in my sketch.