Reassigning library defaults without changing the header

What is the proper way for a sketch to redefine a library's defaults?

I am making a library for taking pin inputs and applying multiple modifications before returning a pseudo pin state. An example would be repeating a button pressed event on a timer while the actual button is just being held down.

In different sketches I would like to be able to declare a different default repeat rate for all instances of the class. So far I can only see 2 ways to go.

  • Modify the constructor in the library header to suit before compiling
  • Define static variables for the constructor to read and a static function to initialize them

Option 1 is simpler but means a different version of the library for each sketch that needs different defaults. Option 2 means undefined behaviour from the constructor until the static function is called to init the defaults.

Am I missing a better option 3?

Option 1 is simpler but means a different version of the library for each sketch that needs different defaults.

I disagree. If you don't like the default, simply override it.

Option 2 means undefined behaviour from the constructor until the static function is called to init the defaults.

I can't see how a static method is going to be better than a class method. I don't see how static fields are going to be better then member fields. I can't see why a static function to manipulate existing instances is going to be better than a member function to initialize each instance. In other words, I don't see option 2 as an option.

simply override it

I can do that? How do I override defaults that are sitting as literal values in the default constructor in the library header?

I realise I can use an overload constructor or member function to pass different settings to an instance. I'm talking about changing the default values that all new instances will use.

A static function and static variables is better because I can change preferred settings once in the sketch and have them applied to all subsequent instances. A non static member function has no power to change what values the constructor will use.

My trouble is I can't have a static .Begin style member function and also set some initial generic defaults with literals.

... Wait. I just read that I can initialise static variables from the .cpp file outside of any member function. That might fix it.

Thanks for your input.

How do I override defaults that are sitting as literal values in the default constructor in the library header?

If I have a class:

class Useless
{
public:
    Useless(int pin = 13);
private:
   int _pin;
}

The constructor has a default value that it will assign (if I implement the constructor correctly) to _pin.

You can create an instance of my class:

Useless doesNothing;

The doesNothing instance will do nothing with pin 13.

You can create an instance of my class:

Useless evenLessUseful(10);

The evenLessUseful instance will do nothing with pin 10.

If you have specific questions about a specific library, now would be the time to trot them out.

PaulS: I can't see how a static method is going to be better than a class method. I don't see how static fields are going to be better then member fields. I can't see why a static function to manipulate existing instances is going to be better than a member function to initialize each instance. In other words, I don't see option 2 as an option.

If you only want to have to call the method once and have it apply to all instances of the class then static would be the way to go would it not?

For example, I have a class that needs to know how long my LCD is so it knows how big a buffer it can send to be printed on the LCD. In the class there is a static int that holds this info and a static method to set it. I might have 5 or 6 instances of this class in a sketch, but I only need call once during setup, ClassName::setNumberOfChars(16); and every instance, even the ones I haven't created yet, knows that I have a 16 character max on the display.

If I made that a non-static member function and variable I'd have to call it once for each instance. That would be silly if I want it to apply to all instances equally now wouldn't it.

If I made that a non-static member function and variable I'd have to call it once for each instance. That would be silly if I want it to apply to all instances equally now wouldn't it.

Yes. But, we need feedback from OP to determine his/her real needs.

I found the best name for what I wanted - a static constructor. C++ doesn't specifically have such a thing but there are a few ways to achieve the desired effect. The simplest being public static member variables in the header that are initialised in the .cpp and can be modified directly during the declarations section of your sketch.

matmanj:
Option 2 means undefined behaviour from the constructor until the static function is called to init the defaults.

You shouldn’t really be doing non-trivial stuff in the constructor anyway. The recognized way in the Arduino world is to use a begin() member function.

So you can construct as many instances of your class as you want, and then call begin() for each one to initialize them. Now you could either pass this variable (the repeat rate) down to each begin() call, or you could set up this static class variable prior to calling begin().

What's wrong with Paul's Useless example? If you want to use a different value in different sketches, just pass a different value. If you're going to be changing the "default" between sketches, it's not a good candidate for a default value. Pass it an explicit value each time.

There's nothing very wrong with it. PaulS knows what he is doing. :P

The only criticism I would make is that instances of classes like that are often global in scope, so his example:

Useless evenLessUseful(10);

... is probably going to done globally (ie. not in a function).

Now if you know at compile time you want pin 10, fine. If this is something you determine at run time, then this is harder to do because the class has been instantiated before you get to setup().

What’s wrong with Paul’s Useless example?

I lolled.

Y’all keep describing how to pass default values to an instance of a class. I know how to do this. What I wanted was a way to change the default values before any instances are created.

I have now done this with static member variables that are initialized in the library .cpp and used as defaults for the constructor. They can be modified by a sketch using a ::Begin static function before any instance is created.

Thanks for your assistance. I have my solution.

matmanj: What I wanted was a way to change the default values before any instances are created.

I have done this with static member variables that are initialized in the library .cpp and used as defaults for the constructor. They can be modified by a sketch using a ::Begin static function before any instance is created.

Yes, but you need to be confident that this static function is executed before the instances are created. You may be confident, or it may just happen to work this time.

The order of instantiation of global variables in different compilation units is not guaranteed.

See: http://www.parashift.com/c++-faq/static-init-order.html

True. That was my initial question really. How do I prevent the possibility of the constructor running with uninitialized variables?

Since I'm only using plain standard datatypes the answer is just to initialise them from the library .cpp. That way ::Begin() is entirely optional and only necessary if one wants different defaults to those already assigned in the library.

For the sake of completeness - I did think of option 3, not convinced it's better. Use an overload constructor and/or .Begin() function to configure 1 instance the way I like and then make copies of the object.

How do I prevent the possibility of the constructor running with uninitialized variables?

You can't. That's why you need to supply reasonable default values. Or, unreasonable ones that mean, to the instance "I really don't want you to do anything when you have this value for this variable".

That way ::Begin() is entirely optional and only necessary if one wants different defaults to those already assigned in the library.

The convention is lower case b.