C++ templates

Pretty sure they were shut by 9pm.

Required planning. What a concept!

Rarely saw the latest programming books in 'em either.

But, they could be obtained with an inter-library loan. Yeah, I know. Requires you to understand ALL the capabilities of the library system.

And a book rarely has an example as varied as the ones you find online.

But, it covers the concepts. Application of the concepts is up to the researcher.

PaulS:

Pretty sure they were shut by 9pm.

Required planning. What a concept!

Consistent condescending belligerence and sarcasm from a forum oldbie, what a concept!

Rarely saw the latest programming books in 'em either.

But, they could be obtained with an inter-library loan. Yeah, I know. Requires you to understand ALL the capabilities of the library system.

Not if they weren't anywhere in the library system. Yes, I know. An inter-library loan required that the book had been bought, entered into the sytem, and was not currently on loan and did not have any holds on it. A less than infinte budget means guess what!? Not every single book was available. ZOMG!! Imagine that!!

And a book rarely has an example as varied as the ones you find online.

But, it covers the concepts. Application of the concepts is up to the researcher.

It might cover the concepts. It's not guaranteed though.

The Internet trumps the library. Hands down.

Now then. How brilliant are void pointers!?

I now have an array of base class pointers that act like any sort of type via template class and function definitions, with a little bit of polymorphism, virtual method and void pointer manipulation thrown in for good measure.

I should be able to go from this point to linked lists, so my array does not need to be defined at compile time to a fixed length.

Tomorrow. :smiley:

I have absolutely no idea what I am doing, but it works for my currently very limited test suite, and that's the main thing:

class baseClass {
    protected:
        bool isDone;
        bool isPin;
    public:
        baseClass() {isDone = false;};
        virtual void debug() {};
        virtual bool isEqual(void *thisVarPtr) {};
};


template <typename T1, typename T2>
class childClass :  public baseClass {
    private:
        T1 variableName;
        T2 lastValue;
        T2 *variablePtr;
    public: 
        childClass(T1 thisVariableName, T2 &thisVariable, bool thisIsPin = false) {
            isPin = thisIsPin;
            variableName = thisVariableName; 
            variablePtr = &thisVariable;
            lastValue = thisVariable;
        };
        virtual void debug() {
            if ((!isDone) || (*variablePtr != lastValue)) {
                isDone = true;
                lastValue = *variablePtr;
                Serial.print(variableName);
                Serial.print(" == ");
                Serial.println(*variablePtr);
            }
        };
        virtual bool isEqual(void *thisVarPtr) { return (thisVarPtr == variablePtr); };
};

template <typename T1, typename T2>
baseClass *newChild(T1 thisVariableName, T2 &thisVariable) {
    return new childClass<T1, T2>(thisVariableName, thisVariable);
};

int varCounter = 0;
baseClass *bArray[10];

template <typename T>
void debug(T &thisVariable) {
    for (int i = 0; i < varCounter; i++) {
        if (bArray[i] -> isEqual(&thisVariable)) {
            bArray[i] -> debug();
            break;
        }
    }
};

Something tells me I am going to have to specialise some templates where a memcopy is required to save the last println'd value of a variable. I can modify the comparison portion of the debug routine at the same time. I was considering comparing only a limited part of the value in that instance, although a strcmp makes the code look pretty minimal regardless.

Certainly nicer doing it this way than instantiating each version of the functions add / debug(var) / debug(name + var) by hand.

Using void pointers, and memcpy, are basically side-stepping all of the type-checking that has been built into C over the years.

I'm wondering why you need to do this? I've seen threads before where people try to work around perceived language restrictions without stating what their true goal is. Once revealed, there is usually an easier way to do things.

I am storing references to variables in order to see if they have changed and act accordingly.

This site has been the pretty useful: http://www.cprogramming.com/tutorial/template_specialization.html

Partial template specialization. You can specify types but also values for the template. First time I saw that. That's really cool.

The example he provides

template <typename T, unsigned length>
class fixedVector { ... };

is immediately useful for my exercise. :smiley:

That page also answers a question I had been considering, and already decided on the most logical answer.

A final implementation detail comes up with partial specializations: how does the compiler pick which specialization to use if there are a combination of completely generic types, some partial specializations, and maybe even some full specializations? The general rule of thumb is that the compiler will pick the most specific template specialization--the most specific template specialization is the one whose template arguments would be accepted by the other template declarations, but which would not accept all possible arguments that other templates with the same name would accept.

aarondc:
I am storing references to variables in order to see if they have changed and act accordingly.

More detail?

Start reading from Debugging library onwards: [Update] Tellurium: serial monitor - Libraries - Arduino Forum

For the people perhaps reading this thread who are actually interested in templates, or at least curious as to what they do, here's an example.

My old header file looked like this:

	void addDebugVar(String, String&);
	void addDebugVar(String, long&);
	void addDebugVar(const __FlashStringHelper *, int&, int = false);
	void addDebugVar(String, byte&, int = false);
	void addDebugVar(String, int&, int = false);
	
	void addDebugPin(const __FlashStringHelper *, int&);
	void addDebugPin(String, byte&, int);
	void addDebugPin(String, int&);
	void addDebugPin(String, byte&);
	
	void debug(String, String);
	void debug(String, long);
	void debug(String, byte);
	void debug(const __FlashStringHelper *, int);
	void debug(String, int);
   	void debug(String &);
	void debug(byte &);
	void debug(long &);
	void debug(int &);

The basic pattern of the functions is:

void [function name](, ); AND
void function name;

The could be supplied as a String, or pointer to char, or F() macro, etc.
The could be any basic type, including - potentially - arrays.

The header file is obviously incomplete, as each function requires versions for the different types, at least 2 of (char * and F()) and then a version of the function for each variable type people may be using. And I don't even know all the variable types! Then there are different functions (overloaded, of course) for handling whether the variable being added is a pin or a normal variable. etc, etc, etc.

The same header file, using templates, can be written as follows:

template <typename T1, typename T2>
void addDebugVar(T1, T2);

template <typename T1, typename T2>
void addDebugPin(T1, T2);

template <typename T1, typename T2>
void debug(T1, T2);

template <typename T1>
void debug(T1);

The beauty of this new header file, is that every possible combination of type and type is generated for you, on the fly, based on usage of calls to those functions in the code. If you don't call that version of the function, it doesn't get compiled.

Very cool.

If this is old hat to you and you have a good website or ebook describing C++ templates, with examples, I really would appreciate a link!

Bit of a dead thread, but if anyone's still reading it, I'd recommend www.learncpp.com . Decent section on templates and very well written and approachable style to all the basic C++ concepts.
Cheers,
Jon

blckdmp:
Bit of a dead thread, but if anyone's still reading it, I'd recommend www.learncpp.com . Decent section on templates and very well written and approachable style to all the basic C++ concepts.
Cheers,
Jon

Thanks for the heads up, much appreciated.

Hi there,

I have a question that particularly pertains to a couple of templates written by Nick Gammon, but I'll explain my scenario first.

I manufacture vision switcher control panels, originally based on Kasper Skaarhoj's fantastic work (we still collaborate on the libraries though they're still 99% his team's work).

My switchers run on the Mega 2560 with Ethernet (Freetronics EtherMega - specifically). I've optimised my libraries and menu code to a great extent and while I'm only about halfway through the program space, I'm running very lean on RAM.

A new model I'm developing includes a graphic OLED display, a big step up from the character OLED Parallel and more recently I2C displays I've been working with to date.

Graphic displays of this resolution (256x64) require a fairly large amount of memory to handle the character sets in different fonts and buffer etc; so my grand plan was to piggyback a second Arduino as a dedicated graphics processor. I've tested both I2C and SPI methods (from gammon.com.au - thanks Nick!) and have inter-arduino communication up and running, however this is where I've hit a template snag.

I would like to have the master send the slave a command string with a header that I'll parse in order to interpret the data that follows. For character displays, I've written into my libraries methods such as OLED.leftAlign(string,length,row,column), and on the graphic displays I'll need to send strings such as OLED.drawBox(topLeftX,topLeftY,bottomRightX,bottomRightY).

The forum page (Gammon Forum : Electronics : Microprocessors : SPI - Serial Peripheral Interface - for Arduino) demonstrates how to send a string and how to send a range of other numeric data types; but I can't figure out how to get the template to accommodate both strings (or character arrays) as well as integers and floats.

I've tried reading up on templates, but I'm not getting far.

This the template library file from the forum at gammon.com.au:

#include <Arduino.h>

template <typename T> unsigned int SPI_writeAnything (const T& value) {
 const byte * p = (const byte*) &value;
     unsigned int i;
     for (i = 0; i < sizeof value; i++) SPI.transfer(*p++);
     return i;
}  // end of SPI_writeAnything

template <typename T> unsigned int SPI_readAnything(T& value) {
    byte * p = (byte*) &value;
    unsigned int i;
    for (i = 0; i < sizeof value; i++)
          *p++ = SPI.transfer (0);
    return i;
  }  // end of SPI_readAnything
  
template <typename T> unsigned int SPI_readAnything_ISR(T& value) {
    byte * p = (byte*) &value;
    unsigned int i;
    *p++ = SPDR;  // get first byte
    for (i = 1; i < sizeof value; i++)
          *p++ = SPI.transfer (0);
    return i;
  }  // end of SPI_readAnything_ISR

Any help much appreciated !

Ta.

I can't figure out how to get the template to accommodate both strings (or character arrays) as well as integers and floats.

You'll need to use a char array, not a char* as only the array will contain size information. A pointer will send two bytes - the pointer address.

Sending an int or float should be straight forward. You shouldn't need to figure out the template, those functions are designed to accommodate different types without any extra effort on your part.

Thanks pYro_65, these templates already accept int & float just fine; But while it does compile when I try char arrays, I don't get any output at the slave end (and not for ints or floats either, so I suspect it's breaking at the master end).

The method that Nick uses for char arrays is a bit different:

	char c;
	for (const char * p = "Hello, world!\n" ; c = *p; p++) SPI.transfer(c);

Obviously that example doesn't read an input but rather sends a specific string; but I thought perhaps I need an if statement within the template that checks typename and sends a char type to the above for loop.

I tried researching if statements within templates and pulled up short. A lot of talk about overloading vs specialisation etc, and some if statements that when I tried them wouldn't compile.

I don't think this template as it stands will accept char arrays.

Any ideas?

I don't think this template as it stands will accept char arrays.

Yes it does accept arrays.

You can verify it with this sketch. It is the same function modified for Serial instead of SPI:

template <typename T> unsigned int Serial_writeAnything (const T& value) {
 const byte * p = (const byte*) &value;
     unsigned int i;
     for (i = 0; i < sizeof value; i++) Serial.write(*p++);
     return i;
}

void setup() {

    Serial.begin(9600);

    char arr[] = "A test string";

    Serial_writeAnything(arr);

}

void loop() {}

The forum page (Gammon Forum : Electronics : Microprocessors : SPI - Serial Peripheral Interface - for Arduino) demonstrates how to send a string and how to send a range of other numeric data types; but I can't figure out how to get the template to accommodate both strings (or character arrays) as well as integers and floats.

You are having a hard time doing that because it is impossible. A template defines a cookie cutter class for ONE type, char array, int, int array, float, OR float array.

There is a type that you could use (struct) that allows combining data of different types. Then, the template would only need to deal with one type, which it can do.