Passing a Structure to a Function

Background:
This all started when I thought it would be a really good idea from a program organization viewpoint to move the setup of the board’s digital I/O pins from the sketch’s .h file to a subroutine (or method, or function, or subfunction whatever you wish to call it.) within a library. On a side note I also need to move the current setup() and loop() codes to a combination of subfunctions and libraries. Having all the code in one giant file is not practical.

I am not all that fluent in C or C++ (older high level languages are more my thing.) so I looked at countless web pages talking about passing structures to subroutines. Most of what I found worked well when the subroutine was in the same physical file as the sketch. (For example xFileA.ino, xFileA.h shown below.) Where I ran into trouble was when I tried to move the subroutine (SetBoardIOPinMode.cpp) first to the same directory level as the sketch and then into a separate library.

I need some help in determining what I am doing wrong, and subsequently fixing these errors.

I am using the Arduino IDE v1.8.12 under MacOS 10.10.5

The physical file architecture is:

  • For xFiles.ino ← Uses a library call for the subroutine
    Sketchbook/xFiles
    xFiles.ino
    xFiles.h
    Sketchbook/libraries/TestLib
    TestLib.cpp (contains SetBoardIOPinMode.cpp)
    TestLib.h (contains SetBoardIOPinMode.h)

  • For xFilesA.ino ← Has all files in one physical file (except .h)
    Sketchbook/xFilesA
    xFilesA.ino
    xFilesA.h

  • For xFilesB.ino ← Has separate files in the same directory
    Sketchbook/xFilesB
    xFilesB.ino
    xFilesB.h
    SetBoardIOPinMode.cpp

So when I compile this I get the same error with xFiles and xFilesB. There are no errors with xFilesA.

Error Messages:

Errors for xFiles.ino:
Arduino: 1.8.12 (Mac OS X), Board: “Arduino Due (Programming Port)”

/Volumes/…/SketchBook/libraries/TestLib/TestLib.cpp: In member function ‘int TestLib::SetBoardIOPinMode(globalPinConnections*)’:
/Volumes/…/SketchBook/libraries/TestLib/TestLib.cpp:32:32: error: invalid use of incomplete type ‘struct globalPinConnections’
int Status = PinConnections->pwmSwingCmdPin;
^
In file included from /Volumes/user01/petah/MechanicalBugs/SingleBoardComputers/Ardunio/SketchBook/libraries/TestLib/TestLib.cpp:5:0:
/Volumes/…/SketchBook/libraries/TestLib/TestLib.h:14:32: error: forward declaration of ‘struct globalPinConnections’
int SetBoardIOPinMode(struct globalPinConnections PinConnections);
^
/Volumes/…/SketchBook/libraries/TestLib/TestLib.cpp:33:28: error: request for member ‘pwmSwingCmdPin’ in ‘PinConnections’, which is of pointer type 'globalPinConnections
’ (maybe you meant to use ‘->’ ?)
pinMode(PinConnections.pwmSwingCmdPin, OUTPUT);
^
exit status 1
Error compiling for board Arduino Due (Programming Port).

Errors for xFilesA.ino: None.

Errors for xFilesB.ino:
Arduino: 1.8.12 (Mac OS X), Board: “Arduino Due (Programming Port)”

sketch/TestMethod.cpp: In function ‘int TestMethod(globalPinConnections*)’:
TestMethod.cpp:8:32: error: invalid use of incomplete type ‘struct globalPinConnections’
int Status = PinConnections->pwmSwingCmdPin;
^
TestMethod.cpp:6:23: error: forward declaration of ‘struct globalPinConnections’
int TestMethod(struct globalPinConnections PinConnections)
^
TestMethod.cpp:9:28: error: request for member ‘pwmSwingCmdPin’ in ‘PinConnections’, which is of pointer type 'globalPinConnections
’ (maybe you meant to use ‘->’ ?)
pinMode(PinConnections.pwmSwingCmdPin, OUTPUT);
^
exit status 1
invalid use of incomplete type ‘struct globalPinConnections’

This report would have more information with
“Show verbose output during compilation”
option enabled in File → Preferences.

SetBoardIOPinMode.cpp (246 Bytes)

TestLib.cpp (934 Bytes)

TestLib.h (601 Bytes)

xFiles.h (537 Bytes)

xFiles.ino (505 Bytes)

xFilesA.h (509 Bytes)

xFilesA.ino (488 Bytes)

xFilesB.h (509 Bytes)

xFilesB.ino (597 Bytes)

might be better if you posted the complete compiler error listing

Do you use an other editor ? Can you set its tabsize to 2 please ?

When you declare define a struct, it is already a typedef.
I think you can do this:

struct myStruct
{
  int a;
  int b;
  float g;
};

void myFunction( myStruct *p)
{
  Serial.println( p->g);
}

You should never declare a variable in a *.h file. You do that a lot. You have to move each and everyone into a *.cpp file.

Add defines to your *.h files to include it just once.
Example: EEPROM.h

#ifndef EEPROM_h
#define EEPROM_h

...

#endif

The Arduino libraries are already a software layer. The code often stays simpler with the Arduino functions. You are making an extra layer, but that can have bugs.

The voltage of 3.28 seems weird. Is that the voltage of the voltage regulator ? It might change a little. Do you know that you forgot the half bit ? https://www.gammon.com.au/adc.
That half bit is handy when taking the average of many samples.

[EDIT] The typedef of a struct is a define (I think).

Koepel:
You should never declare a variable in a *.h file by using extern.

Then you must define the variable in a .cpp file. See: What's the difference between declaring and defining in C and C++ - Cprogramming.com

All.

I wanted to thank everyone who replied to my quandary. I’ll try to answer each question.

gcjr:
That was the whole compiler error listing although I did remove most of the paths and replaced the removed part with ‘…’ I am not sure whatelse is available.

Koepel:
The reason I was defining variables in the .h files was to keep the main body a reasonable size (# of lines) to work with. This also helped make things more readable. That being said, I do get your point.

I am not sure I really understand what ‘typedef’ means. I am just beginning to wrap my brain around ‘define’ and ‘declare.’

Do you use a different editor? Actually I use the Arduino IDE because it came with the Arduino and interfaces well with the chip. And besides I like the colorations. What can I say? You are right about the tabs. I did not see a way to set them. Is there another IDE people use?

Thanks for the suggestion to use the ‘#ifndef’ block. It did save my butt from lots of redefining errors. That got straightened out.

I am using the extra layers to keep the modular without ending up with 1000 line program listings. Using libraries seemed one way to do it. Also since I am using the Arduino IDE, using libraries, helped keep the number of tabs across the screen to a manageable number.

The 3.28 comes from the Arduino Due. Apparently it is a 3.3v process and has regulated 3.3(?) and 5v outputs available. Since I was going to use the 3.3 as a reference, I thought I should measure it, and it turns out to be 3.28v. There are lots of warnings about not applying voltages greater than 3.3 to the Due’s pins.

gfvalvo:
I did what you said about declaring a variable in the .h file external. It worked! (And your saying to yourself: Of course is worked.) The website (What's the difference between declaring and defining in C and C++ - Cprogramming.com) you suggested was invaluable. Thanks!


I went back and modified the xFiles program I had originally uploaded. I can certainly upload the finished copy so others can see what I did.

The uploaded files are: GlblStruct.h, Output.txt, TestLib.cpp, TestLib.h, xFiles.h, xFiles.ino

Thanks again for all of yours help.

GlblStruct.h (272 Bytes)

Output.txt (224 Bytes)

TestLib.cpp (1.45 KB)

TestLib.h (670 Bytes)

xFiles.h (233 Bytes)

xFiles.ino (1.01 KB)

Koepel:
The reason I was defining variables in the .h files was to keep the main body a reasonable size (# of lines) to work with. This also helped make things more readable. That being said, I do get your point.

H and CPP files have different functions. You can't move lines between them willy-nilly. Looks like you got that sorted out now, but keep that in mind for future projects.

Also

pwmScale = 127.0 / 511.0;

This should be 128.0 / 512.0. Standard fence-post error*, using the maximum value instead of the total number of counts.

Based on the names used in your example, is the input to your ADC a potentiometer? If the potentiometer is being supplied the same voltage as the Due board, you don't need to perform voltage conversions. You can just use the ADC's return value and compare it against 1024 to figure out what % of full scale it is.

  • If you don't know what a fence-post error is, it comes from this question: If I want to build a 100m fence with a post every 1m, how many fence posts will I need?

That is better :smiley:

A 'int' and a 'float' are types. Suppose that you can make your own type: Fruit
That is possible with a "typedef". A typedef is often used together with a struct.

typedef struct Fruit_STRUCT
{
  int color;
  int weight;
} Fruit;

Fruit medlar;

void setup()
{
  medlar.color = 3; 
}

The typedef can be omitted, and then it is:

struct Fruit
{
  int color;
  int weight;
};

Fruit medlar;

void setup()
{
  medlar.color = 3; 
}

You do that already. I'm too lazy to check your first files if you had that there :wink:
You use "by reference" instead of pointers. Very nice.

The xFiles.h has still a variable:

const unsigned long BAUD_RATE = 19200;

There are tabs in the files and sometimes indents of 4 spaces. Can run a search and replace tool ?
My Arduino IDE replaces tabs with spaces when saving the file.
Can you try this:
Arduino IDE 1.8.13 menu: File / Preferences
At the bottom is the location of preferences.txt in the hidden arduino15 folder.
Click on it to go to that folder, close the Arduino IDE.
Is there a file "formatter.conf" which does not have the default line of "indent=spaces=2".

Does typdef really have any value in c++, particularly when applied to a struct?? I can't think of any. Unlike in c, a struct is inherently a "type", virtually identical to a class. So, while use of "typedef struct" is perfectly legal, it seems to me also perfectly pointless. And even for other "types", I'm not sure I see a need for typedef in c++. Most, if not all, places where it might be normally used seems to me better suited to use of enum, rather than typedef. Am I missing something?

RayLivingston:
Am I missing something?

No, of course not.

I didn't mention old 'C' versus modern 'C++' and no need for typedef. I was reacting to the files in the first post which had parameters such as: struct something *

RayLivingston:
So, while use of "typedef struct" is perfectly legal, it seems to me also perfectly pointless. And even for other "types", I'm not sure I see a need for typedef in c++. Most, if not all, places where it might be normally used seems to me better suited to use of enum, rather than typedef.

The C++ 'using' keyword seems to do a lot of the jobs we used to do with typedef. Say you want to define a type that's a pointer to a function. And, said function takes a char * argument and returns a uint16_t.

typedef:

typedef uint16_t (*typedefPointer)(char *);

using:

using usingPointer = uint16_t (*) (char *);

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.