Go Down

Topic: Select behavior (methods) at compile time (Read 328 times) previous topic - next topic

dougp

With the assistance of the forum I have developed a timer class and a library to hold it.  There are currently three timer variants available through one instantiation.  I'd add more choices but the logic to run the timer and control the output based on timer type gets ugly quickly using only one basic skeleton - lots of ifs and logical compares. An example of what's possible. 

What I think should be possible is to create a number of timer types all under the umbrella of one class, each with its own behavior, and select one of those options at compile time.  For instance, every timer type will have an input called 'enable' which initiates and/or controls the particular timer's behavior,  and an output 'done' which tells the world the timer has responded to the input, which response will vary depending on type.

Any of the available types would be selected by: PLCtimer( type, preset);

An example: standardTimer will accumulate time when enable is true and done returns true when preset is reached.  It resets to zero accumulated value and done returns false when enable is false.

latchedTimer - will start accumulating time when enable goes true but thereafter is independent of enable, it continues timing 'til reaching preset whereupon done returns true and remains true until a false-to-true transition is seen at enable.  This event resets the timer.

The questions:

What is doing something like this (choosing one option from several at compile time) called?

Are there multiple ways to skin this cat?

May I assume the compiler will optimize away the unused blocks of code and only install that which is selected?

Thanks for looking.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

DKWatson

You are versed in the pre-compiler directives?
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

dougp

#2
Aug 10, 2018, 06:07 am Last Edit: Aug 10, 2018, 06:10 am by dougp
You are versed in the pre-compiler directives?
Ummmm. That would be a "No".

Is that something distinct from 'pre-processor' directives?
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

DKWatson

I've attached the gcc pre-compiler manual. Have a look through that and we can discuss it further. Essential there are a host of conditional compilation directives designed to do exactly what you want. What must be established prior to of course, is the condition against which you will test. As a quick example,
Code: [Select]
#ifdef __AVR_ATmegamega2560__
    #define     LED_PORT    PORTB
    #define     LED_PIN     PB7
    const byte BOARD[]      = {"mega2560"};
#endif

#ifdef __AVR_ATmega328P__
    #define     LED_PORT    PORTB
    #define     LED_PIN     PB5
    const byte BOARD[]      = {"328P"};
#endif
will set the LED port based on the board. The macros __AVR_ATmega328P__ and __AVR_ATmegamega2560__ are defined in the board specific header.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

DKWatson

Pre-compiler, pre-processor, I guess it depends on your generation. I'm too old.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

dougp

I've attached the gcc pre-compiler manual. Have a look through that and we can discuss it further.
Thanks!  Reading now.  This is more accessible than a lot of other language/syntax descriptions.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

PaulMurrayCbr

Normally, you'd have three classes that implement virtual methods.

To do what you describe, you could use a 'strategy' pattern. The timer class would be a wrapper around an implementation which would be created in the constructor.

Code: [Select]

class ColorPrinter {
    class Impl {
      public:
        virtual void printMe() = 0;
    };

    class RedImpl : Impl {
        void printMe() {
          Serial.print("RED");
        }
    };

    class GreenImpl : Impl {
        void printMe() {
          Serial.print("GREEN");
        }
    };

    const Impl *impl;

  public:

    ColorPrinter(boolean useRed) : impl( useRed ? (Impl*) new RedImpl() : (Impl*) new GreenImpl()) {
    }

    void print() {
      impl->printMe();
    }

};

ColorPrinter redPrinter(true);
ColorPrinter greenPrinter(false);

void setup() {
  Serial.begin(9600);

  Serial.print("redPrinter.print() = ");
  redPrinter.print();
  Serial.println();

  Serial.print("greenPrinter.print() = ");
  greenPrinter.print();
  Serial.println();
}

void loop() {
}
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

dougp

Normally, you'd have three classes that implement virtual methods.

To do what you describe, you could use a 'strategy' pattern. The timer class would be a wrapper around an implementation which would be created in the constructor.
Might be a non-starter since 'virtual methods' are unknown to me.  Looking it up now.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

johnwasser

Rather than having one class that behaves in different ways, perhaps you should have a base class and derived classes, each of which behaves in one way. That would be easier to follow.

Code: [Select]
class PLCtimer;
class PLCLatchedTimer : PLCTimer;
class PLCStandrdTimer : PLCTimer;


Read about "inheritance": http://www.cplusplus.com/doc/tutorial/inheritance/
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

dougp

Thanks for all the replies.  I do appreciate it.  The suggestions so far:

You are versed in the pre-compiler directives?
Normally, you'd have three classes that implement virtual methods.
Rather than having one class that behaves in different ways, perhaps you should have a base class and derived classes, each of which behaves in one way. That would be easier to follow.
At this point preprocessor directives seem the most accessible to me because I have some understanding of  if/else  and  ifdef/ifndef.  I'm guessing these two would play a key role in what I hope to do.

I've been reading but other two approaches - @PaulMurrayCbr's virtual methods and @johnwasser's derived classes - are stretching me.

From the standpoint of keeping the stated PLCtimer(type, preset); as the creator of the various types, how do each of the offered solutions accomplish this?  Reading the tutorials/docs I can't see how type influences which sort of timer is generated.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

johnwasser

From the standpoint of keeping the stated PLCtimer(type, preset); as the creator of the various types, how do each of the offered solutions accomplish this?  Reading the tutorials/docs I can't see how type influences which sort of timer is generated.
You can't change the declaration of the object at run time.  All you can do with a "type" argument to the constructor is to store the 'type' and have the methods behave differently depending on the type.  In your cases of StandardTimer and LatchedTimer types the differences appear to be in the handling of "enable" changes.  Just use 'if' or 'switch' statements to tailor behavior of each method to the current timer 'type'.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

dougp

You can't change the declaration of the object at run time.
Right, I don't want to be able to change it on the fly.  What I'm after is some mechanism by which the compiler recognizes the type and keeps only code to create that type while discarding/optimizing out irrelevant code.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

dougp

I've attached the gcc pre-compiler manual. Have a look through that and we can discuss it further.
I've looked over the conditionals in the manual - and saved a copy of the .pdf.  I'm ready to see how this might be done.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

DKWatson

Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

dougp

Let us know how you make out.
I was referring to your post #3.  If I could understand how to make the preprocessor aware of the type variable I feel I could make some progress.  Presently I don't see any way to make that happen.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

Go Up