How to define values at compile time based on template argument

Hi!

I'm making a really simple library to control my stepper motors, and it's also for learning, I'm pretty sure there's already a lot of libraries for this. As I want to make it as efficient as possible, my idea is to use templates to avoid storing the pin addresses in variables and so on. The stepper is a BYJ28-48, and the way I control it is by going through a list of "coil status" (Yeah, I'm not sure how to call it). Anyways, so I can use different modes for that, for example, I can enable one coil at a time, or two at a time, or I can mix both methods and enable the first, then the first and second, then the second only, and so on...

What I used to do before was this:

	#if MOTOR_MODE == MODE_PHASE_SINGLE
		#define MOTOR_STEPS	4
		const uint8_t motor_step_table[MOTOR_STEPS] = {B1000, B0100, B0010, B0001};
	#elif MOTOR_MODE == MODE_PHASE_DUAL
		#define MOTOR_STEPS	4
		const uint8_t motor_step_table[MOTOR_STEPS] = {B1100, B0110, B0011, B1001};
	#elif MOTOR_MODE == MODE_PHASE_HALF
		#define MOTOR_STEPS	8
		const uint8_t motor_step_table[MOTOR_STEPS] = {B1000, B1100, B0100, B0110, B0010, B0011, B0001, B1001};
	#else
		#error you must define MOTOR_MODE as MODE_PHASE_SINGLE, MODE_PHASE_DUAL or MODE_PHASE_HALF
	#endif

And I would use that array (and the const MOTOR_STEPS) in the sketch. As I'm doing it in a library now, I'm not quite sure how I can imitate that same behavior when using templates. The idea would be to pass the MOTOR_MODE value to the initializator list and then have the compiler figure it out (somehow, I'm not even sure if it's technically possible, but I guess there's a workaround). Sure, I could have the constructor set those values (and yeah, I would be losing around 4 + 1 bytes of RAM space), but I actually want to learn if this is possible.

My code [NoDriverStepper.h]:

#ifndef NODRIVERSTEPPER_N
#define NODRIVERSTEPPER_N

	/** 
	 *	Possible modes: 
	 *		MODE_PHASE_SINGLE:	one phase at a time (4 steps)
	 *		MODE_PHASE_DUAL:	two phases at a time (4 steps, more strength)
	 *		MODE_PHASE_HALF:	half step (8 steps, more precise, slightly less strength, uses one and two phases depending on the step)
	 */

	#define MODE_PHASE_SINGLE	1
	#define MODE_PHASE_DUAL		2
	#define MODE_PHASE_HALF		3

	#if ARDUINO >= 100
		#include "Arduino.h"
	#else
		#include "WProgram.h"
	#endif

	#define NDS_TEMPLATE		uint8_t MOTOR_PIN1, uint8_t MOTOR_PIN2, uint8_t MOTOR_PIN3, uint8_t MOTOR_PIN4, uint8_t MOTOR_MODE = MODE_PHASE_HALF
	#define NDS_TEMPLATE_NOTYPE	MOTOR_PIN1, MOTOR_PIN2, MOTOR_PIN3, MOTOR_PIN4, MOTOR_MODE
	
	template <NDS_TEMPLATE>
	class NoDriverStepper
	{
	private:
		// any way to define an array here?
		// It would be of a fixed length (either 4 or 8 bytes), depending on MOTOR_MODE value (1 and 2 = 4 bytes), 
	public:
		
	};
#endif

Thank you all! :slight_smile:

I have not tried to figure out your post in detail and I am unclear exacxtly what you mean by templates, but my thoughts are;

You need to consider exactly what your program is and what happens at each stage e.g. source code, pre-compile time, compile time, execution.

It is possible to use preprocessing blocks to conditionally determine what your the source code that will eventually be compiled looks like. What you can do with preprocessing is very wide but it all happens before the compiler sees the code.

After pre-processing the compiler gets to work and allocates storage etc. for the code it has been presented with (i.e. too late to change array sizes or remove functions or variables). When the sketch executes it again only use what has been compiled.

I imagine you could do it with Template Specialization --- kind of like how FastLED supports many different types of LED strips.

But, I'm not skilled in the art. One or two folks who contribute here are. Perhaps one of them will chime in.

First of all, instead of

#define MODE_PHASE_SINGLE 1
#define MODE_PHASE_DUAL 2
#define MODE_PHASE_HALF 3

You migh want to use an enum (1), or strong enum (2):

(1)
enum{ MODE_PHASE_SINGLE=1, MODE_PHASE_DUAL, MODE_PHASE_HALF };

(2)
enum class: uint8_t{ MODE_PHASE_SINGLE=1, MODE_PHASE_DUAL, MODE_PHASE_HALF };

Option (2) it's safer.

Regarding to templates, use Template specialization:

template
struct C
{
int array[ len ];
};

...

C<4> anArray;
C<8> otherArray;

fjrg76:
Regarding to templates, use Template specialization:

template
struct C
{
int array[ len ];
};

...

C<4> anArray;
C<8> otherArray;

That's not Template specialization. That's simply instantiating arrays based on a template.

Maybe this is what you want:

template <size_t N>
class NoDriverStepper
{
public:
    NoDriverStepper(const uint8_t (&in_pins)[N]) :
        pins {}
    {
        for (size_t i=0; i<N; ++i)
        {
            pins[i] = in_pins[i];
        }
    }
private:
    uint8_t pins[N];
};

void setup() {
  // put your setup code here, to run once:
  NoDriverStepper<3> controller({1,3,6});
}

void loop() {
  // put your main code here, to run repeatedly:

}

This should work to replace your MOTOR_MODE macros.

enum class MotorMode {
    PhaseSingle,
    PhaseDual,
    PhaseHalf,
};

// Primary template
template <MotorMode>
struct MotorConstants {};

// Specializations
template<>
struct MotorConstants<MotorMode::PhaseSingle> {
    constexpr static uint8_t steps = 4;
    constexpr static uint8_t stepTable[steps] = {0b1000, 0b0100, 0b0010, 0b0001};
};

template<>
struct MotorConstants<MotorMode::PhaseDual> {
    constexpr static uint8_t steps = 4;
    constexpr static uint8_t stepTable[steps] = {0b1100, 0b0110, 0b0011, 0b1001};
};

template<>
struct MotorConstants<MotorMode::PhaseHalf> {
    constexpr static uint8_t steps = 8;
    constexpr static uint8_t stepTable[steps] = {0b1000, 0b1100, 0b0100, 0b0110, 0b0010, 0b0011, 0b0001, 0b1001};
};

// Definitions to keep the linker happy (can be inside .cpp file)
constexpr uint8_t MotorConstants<MotorMode::PhaseSingle>::stepTable[];
constexpr uint8_t MotorConstants<MotorMode::PhaseDual>::stepTable[];
constexpr uint8_t MotorConstants<MotorMode::PhaseHalf>::stepTable[];

void output(uint8_t) { /* write to the output pins */ }

void loop() {
    // Select the mode you want to use and give it an alias:
    using Motor = MotorConstants<MotorMode::PhaseHalf>;
    // Use the constants:
    for (uint8_t step = 0; step < Motor::steps; ++step) {
        output(Motor::stepTable[step]);
        delay(25);
    }
}

Pieter

I'm amazed by how many responses I got in such little time, thank you all!

This week my HDD died so didn't had to set up a couple things again before getting to work on this again.

@arduino_new I thought about using the constructor to initialise everything, but I wanted to see if there was a way to optimize it before compiling it. (Yeap, I should have stated it better in the title, my bad). But thanks for the suggestion anyways!

PieterP:
This should work to replace your MOTOR_MODE macros.

@gfvalvo @PieterP Template specialization was exactly was I was looking for, so I'll try to use this concept and I'll publish it in a repo in GitHub (link to the repo).

Another question, why do I need those constexpr? (the ones to "keep the linker happy?"). I guess it's for other code to be able to "see" those definitions, but I'd like to hear a better (or more accurate for sure) explanation.