Library Creation Question - Overloading Constructor or other method?

I’ve been making an “Ultimate Debounce” library based on the article:

DEBOUNCE YOUR NOISY BUTTONS, PART II by: Elliot Williams

I’m still quite new to Library making, so I took this as a seemingly easy, but useful, learning project.

I have it working just fine, per his article and code.

I’ve also added an overloaded constructor to allow per-object definition of active High or Low for the button signals.

My question come in where I’m wanting to implement his 16-bit History idea (Mentioned but no examples).

As the larger history versions (16-bit, 32-bit , etc) use virtually the same code - the only differences come with storing the history in a ‘larger’ memory type and comparing to a different mask.

I believe I can make it work as a separate standalone, Library, however I was hoping to be able to combine everything into a single class, but I don’t know what the best approach would be.

The code, as it stands, is HERE

I have a couple of ideas, but none of them seem to be any better than the others, from my still-learning hobbyist level of understanding:

  • I can pass an argument in a constructor and use a variety of IF statements (or similar) to cover the different options.

  • The only other thing I can think of is to have a seperate class, perhaps UltimateDebounce16 (??), and just copy the same code over, but keep it in the same overall Library.

  • I recently came across the Idea of namespaces, but haven’t tried playing with them yet.

#1 - Seems awkward and not very elegant. I’d rather make this nice, not just something Hacked together.

#2 - Seems like it would be quite awkward for any updating, as I’d have to do it in two seperate places, and a few functions would be identical between them.

#3 - Seems like it may be possible, but I have no idea if/how this would affect the library size or efficiency.

Is any of my ideas considered ‘Better’ than the others?

Is there an even better way to learn about?

Side question - How do I enter a tab, or indent on this post editor???


Below are just some ramblings that may or may not be relevant …


To save going to the linked Github, here is a small example:

Inside the .h file, I have, among others:

	private:
		uint8_t _button_history=0;

And within the .cpp file:

#define MASK8   	0b11000111		// Mask8 0's are "don't care"

#define DOWN8 	0b11111111


uint8_t UltimateDebounce::is_down(void){

	return (_button_history == DOWN8);
}

(I know MASK8 isn’t used in this example function)

With the 16 bit idea, I would need to have:

	private:
		uint16_t _button_history=0;
#define MASK16  		0b1110000000011111		// Mask16 0's are "don't care"			

#define DOWN16 		0b1111111111111111

uint8_t UltimateDebounce::is_down(void){			// if Button is being held active, return true

	return (_button_history == DOWN16);
}

Ideally, I’d like to be able to use it something like:


UltimateDebounce(uint8_t buttonPin, uint8_t activeLevel, uint8_t historySize);


While still being quick, small, and light on the processor.

With various constructor options, such as:


UltimateDebounce someSW1(someSW1_PIN); // Defaults to active High and 8-bit history, on pin someSW1_PIN

UltimateDebounce someSW2(someSW2_PIN,LOW); // 8-bit history, on pin someSW2_PIN, while Active LOW

UltimateDebounce someSW3(someSW3_PIN,LOW, 16BIT);


While still being quick, small, and light on the processor.

(Sorry for not using the code bracket for those last few, but for only a couple of lines, it seems like a huge box on preview.)

Techniques I have used that you have not mentioned:

  1. Virtual classes. The hardware dependencies then can be written separately and common code remains
    common. You can look at my MD_UISwitch library for this technique.

  2. Using the optional 'default parameter' to a method/function to switch between modes. When a parameter is omitted it takes the default and this can be used for the majority of cases (or for upgrade compatibility) while still allowing customisation within the library.

What you want done needs the use of template. Variable type needs to be determined at compile time. With template class, the compile can generate correct class for different type.

Here’s something to help you getting started:

template<typename T>
T GetMask()
{
  switch (sizeof(T)) {
    case sizeof(uint8_t):
      return (T)0b11000111;
    case sizeof(uint16_t):
      return (T)0b1110000000011111;
    default:
      return (T)0;
  }
}

template<typename T>
class Debounce 
{
  private:
    const T MASK;
    T buttonState;
  public:
    Debounce() :
      MASK(GetMask<T>()),
      buttonState(0)
    {
      
    }
};

Debounce<uint8_t> debounce8;
Debounce<uint16_t> debounce16;

void setup() {
  // put your setup code here, to run once:
  
}

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

}

Thanks for the suggestions.

Other tasks are going to be pulling me away shortly, so I may not get a chance to explore them for a while...

In the meantime, I added the 16-Bit history as a second Class within the same Library, so it's at least available if someone wants to use it.

Now you have the option of creating objects as:

UltimateDebounce For 8-Bit
UltimateDebounce16 For 16-Bit

Constructor options, commands, and responses are identical.
.... They should be fully compatible with each other just with changing the initial creation

Again, Thanks for the advice.

If/When I figure out an improvement I'll post it. :slight_smile: