Techniques to split code across multiple files in large projects (I broke my build)

Hey there!

My goal is to master the techniques for splitting code across multiple files on my Photon project.

For months I’ve been typing away at my project called 21 Days, a habit forming picture frame. Now I have around 1000 lines of code all in one cpp/h file, and I plan to add more. The project has namespaced “global” variables, uses particle libraries and external libraries, and a lot of functions.

My project worked at one point, but I’ve now reorganized everything into a broken state. I’m unsure how to proceed, and unable to find guides or examples.

I’d really appriciate it if some of you reviewed my code (before refactor, after refactor), and provided me with some direction.

It would be awesome to come up with a architecture/pattern together for organizing larger Particle projects written in C/C++.

– chris

P.S. Some of the global variables (like strip, matrix and habit) are instances of a class that are only constructed once (more or less constant), and others (primarily state) are mutated all the time.

P.S.S. In case the context is useful, I’ll add that I’m coming from a language background including F#, Typescript (javascript), and Python. This project’s architecture attempts to apply some of my favorite state-machine-like concepts from React and Redux.

I’m developing on OSX using the latest version of Particle Dev desktop IDE. I’m targeting the latest Photon firmware (0.6.2-rc.1). I’ve ruled out the issue being that I’m using numbers at the start my file names, but I changed it to days just in case.

Compiler reports around 30 errors.

Not sure how to copy all of them, but here are the first few.

Unfortunately, the errors don’t source map correctly and open blank files when clicked located in my “workspace” (I’m not sure what that is).

Just a few general rules to address most of your errors:

  • create headers for all your indidual modules and include them where needed.
  • guard the headers against multi inclusion (e.g. #pragma once)
  • for global variables you’d need to have them declared extern (best in a reusable header file) and actually “implement” them in one module only
2 Likes

Thanks for the tips! I have the first and third points, and just added #pragma once to my .h files. The number of errors didn’t go down.

Could you look at habit.h and habit.cpp for noob mistakes? I have a couple files like this where I am defining a typedef, namespaces, and extern and compiler errors.

I tried a different typedef syntax and left it in color.h, but it didn’t seem to help.

What is the general guideline for when to include a file in a group’s .h vs .cpp (i.e. lights.h and lights.cpp) , or is it always safe to just include the files in the .h?

I figured out how to address the error variable or field declared void.

Some of my remaining errors:

In file included from /workspace/src/habit.h:9:0,
                 from /workspace/src/buttons.h:6,
                 from /workspace/src/buttons.cpp:1:
/workspace/src/color.h:6:1: error: expected initializer before 'typedef'
 typedef struct {
 ^
/workspace/src/color.h:10:3: error: 'Color' does not name a type
 } Color;
   ^
In file included from /workspace/src/habit.h:11:0,
                 from /workspace/src/buttons.h:6,
                 from /workspace/src/buttons.cpp:1:
/workspace/src/lights.h:15:16: error: variable or field 'sideOff' declared void
   void sideOff(Habit &config);
                ^
/workspace/src/lights.h:15:16: error: 'Habit' was not declared in this scope
/workspace/src/lights.h:15:23: error: 'config' was not declared in this scope
   void sideOff(Habit &config);
                       ^
/workspace/src/lights.h:16:17: error: variable or field 'todayOff' declared void
   void todayOff(Habit &config);
                 ^
/workspace/src/lights.h:16:17: error: 'Habit' was not declared in this scope
/workspace/src/lights.h:16:24: error: 'config' was not declared in this scope
   void todayOff(Habit &config);
                        ^
/workspace/src/lights.h:17:12: error: variable or field 'off' declared void
   void off(Habit &config);
            ^
/workspace/src/lights.h:17:12: error: 'Habit' was not declared in this scope
/workspace/src/lights.h:17:19: error: 'config' was not declared in this scope
   void off(Habit &config);
                   ^
/workspace/src/lights.h:18:15: error: variable or field 'sideOn' declared void
   void sideOn(Habit &config);
               ^
/workspace/src/lights.h:18:15: error: 'Habit' was not declared in this scope
/workspace/src/lights.h:18:22: error: 'config' was not declared in this scope
   void sideOn(Habit &config);
                      ^
/workspace/src/lights.h:19:16: error: variable or field 'todayOn' declared void
   void todayOn(Habit &config);
                ^
/workspace/src/lights.h:19:16: error: 'Habit' was not declared in this scope
/workspace/src/lights.h:19:23: error: 'config' was not declared in this scope
   void todayOn(Habit &config);
                       ^
/workspace/src/lights.h:20:44: error: 'Habit' has not been declared
   void historyOn(state::HabitState &state, Habit &config);
                                            ^
/workspace/src/lights.h:21:11: error: variable or field 'on' declared void
   void on(Habit &config);
           ^
/workspace/src/lights.h:21:11: error: 'Habit' was not declared in this scope
/workspace/src/lights.h:21:18: error: 'config' was not declared in this scope
   void on(Habit &config);
                  ^
In file included from /workspace/src/buttons.h:6:0,
                 from /workspace/src/buttons.cpp:1:
/workspace/src/habit.h:18:3: error: 'Color' does not name a type
   Color color;
   ^

You have a problem with the order of your declarations.
e.g. in this error

In file included from /workspace/src/habit.h:9:0,
                 from /workspace/src/buttons.h:6,
                 from /workspace/src/buttons.cpp:1:
/workspace/src/color.h:6:1: error: expected initializer before 'typedef'
 typedef struct {
 ^
/workspace/src/color.h:10:3: error: 'Color' does not name a type
 } Color;
   ^

habit.h includes button.h (which in turn includes habit.h) before it includes color.h

In habit.h moving #include "color.h" in front of #include "buttons.h" solves that one error and with it others go away too.

I changed my typedef struct Color Color to just typedef struct Color { ... } following your suggestion for both Color and Habit, and then added struct in front of my signature and definition types (struct Habit = ... vs Habit = .... This made a bunch of errors go away.

I then fixed a forward declaration issue in the Habit struct following this answer, field 'color' has incomplete type.

Moving #include "color.h" did solve that error you mentioned.

Now I’m just left with these errors:

In file included from /workspace/src/habit.h:7:0,
                 from /workspace/src/buttons.h:6,
                 from /workspace/src/buttons.cpp:1:
/workspace/src/color.h:10:1: warning: 'typedef' was ignored in this declaration [enabled by default]
 };
 ^
In file included from /workspace/src/habit.h:10:0,
                 from /workspace/src/buttons.h:6,
                 from /workspace/src/buttons.cpp:1:
/workspace/src/state.h:6:1: error: expected initializer before 'namespace'
 namespace state {
 ^

Great news! I fixed my compiling issues as of commit e012d0.

  • removed circular dependency from habit.h and lights.h (by introducing actions.h for the functions that require both)
  • added missing semi-colons in my forward declarations
  • removed typedef as it is not necessary in C++ on structs
  • added a few missing #include statements
  • correctly dereferenced my pointers, and initialized their values correctly

Now I’ll get to testing and organizing the code a bit more.

My retrospective for others that have issues is to read each of these posts for things to try, look back at my last 7 commits, and use the CLI compiler (particle compile photon) for the best error messages.

3 Likes