function pointer & memory selection

Hi Community,

iam on my way to build a 'communication-interface' between some arduinos (using serial)
the concept is that every arduino knows all data of the hole system.
(changes are 'broadcastet' to every arduino)

at the moment iam writing the heart of the thing - the 'parse incoming / send data / set / get' handler.

on the main thing i use a big switch case to map the incoming id to the internal variable name.
as i have begin to write this big thing i came to the idea to use function pointers and variable pointers
so that in the switch case only the datatype handling is done -
and after this the real action (operation : receive / transmit / set / get) is done.

i have written a striped down sketch to test the concept.

my problem is that i don't know how to best handle the different datatypes that i have
(at the moment word, byte, boolean)

here the test sketch:

/*
	Test sketch for function pointers and data handling 
	
	Public Domain
*/


class xLocalTestClass {
	public:
		// public consts :
			
			static const byte state_NotValid			= 11;
			static const byte state_Standby				= 12;
			
			static const byte hdOperation_SET		= 's';
			static const byte hdOperation_SETINT	= 'i';
			static const byte hdOperation_GET		= 'g';
			static const byte hdOperation_RECEIVE	= 'r';
			static const byte hdOperation_TRANSMIT	= 't';
			
	private:
		// per object data general
			
			// ID
			const byte cbID;
			// flag to check if the begin function is already called and the class is ready to work.
			boolean bReady;
			
			// internal state
			byte bState;
			
			// Internal representation of complete 2ChControl System:
			
			// left right
			struct sSideIO {
				byte bButtonStates;
				byte bButtonEvent[5];
				word wLEDsRaw;
				int iFader_position_current;
				int iFader_position_target;
				byte bFader_touch;
				word wRotaryEncoder_position;
			};
			
			// system
			struct sSystem {
				bool bSlidingSwitch_Left;
				bool bSlidingSwitch_Right;
				byte bSDCard_State;
				byte bPowerMux_State;
				word wPowerIN_Voltage;
				word bInfoLEDsRaw;
			};
			
			// display
			struct sDisplay {
				byte bBGLight_State;
				byte bBGLight_Value;
				byte bContrast_State;
				byte bContrast_Value;
			};
			
			sSideIO memory_LeftRight[2];
			sSystem memory_System;
			sDisplay memory_Display;
			
		// private methods
			
			
			bool checkChanged_Value(word *pMemory, word *pValue, byte bBitIndex) {
				bool bResult = 0;
				if (*pMemory != *pValue) {
					bResult = 1;
				}
				return bResult;
			}
			
			bool checkChanged_2Bit(word *pMemory, word *pValue, byte bBitIndex) {
				bool bResult = 0;
				if (((*pMemory >> (bBitIndex*2)) & 0b0000000000000011) != *pValue) {
					bResult = 1;
				}
				return bResult;
			}
			
			void setValue_Value(word *pMemory, word *pValue, byte bBitIndex) {
				*pMemory = *pValue;
			}
			
			void setValue_2Bit(word *pMemory, word *pValue, byte bBitIndex) {
				if (*pValue){
					// set Bit
					*pMemory |= (*pValue << (bBitIndex*2));
				} else {
					// clear Bit
					*pMemory &= ~(*pValue << (bBitIndex*2));
				}
			}
			
			
	public:
		// Constructor
			xLocalTestClass(
				const byte cbID_New
			) :
				cbID(cbID_New)
			{
				//do some initialisation
				bReady = false;
			};
			
		// Destructor
			~xLocalTestClass() {
			  // nothing to do here ?!
			};
			
			
		// public methods general
			
			// get ID
			const byte getID() {
				return cbID;
			};
			
			
			// check if class is ready to operate.
			boolean isReady() {
				return bReady;
			};
			
			
			// start
			void begin() {
				if (bReady == false) {
					
					bReady = true;
				}
			};
			
			// update system 
			// returns state
			byte update() {
				byte bStateTemp = state_NotValid;
				if (bReady) {
					bStateTemp = bState;
					
				} // end if bReady
				return bStateTemp;
			};
			
		// public methods functional
			
			// start Counting up to an given value; if Value is reached generates an state changed event.
			byte test(byte bOperation, char cSwitchMemory, byte bBitIndex, word *pValue, word wValueReceive) {
				
				// default to left
				byte bLRIndex = 0;
				if ( 1 != 1) {
					// set to right
					bLRIndex = 1;
				}
				
				// pointer to Memory Value
				word *pMemory = NULL;
				
				
				// tCbfuncCheckChanged fc_CheckChanged = NULL;
				bool (xLocalTestClass::*fc_CheckChanged) (word *pMemory, word *pValue, byte bBitIndex) = NULL;
				void (xLocalTestClass::*fc_SetValue) (word *pMemory, word *pValue, byte bBitIndex) = NULL;
				
				switch (cSwitchMemory) {
					case 'a': {
						pMemory = &memory_LeftRight[bLRIndex].wRotaryEncoder_position;
						fc_CheckChanged	= &xLocalTestClass::checkChanged_Value;
						fc_SetValue		= &xLocalTestClass::setValue_Value;
					} break;
					case 'b': {
						pMemory = &memory_LeftRight[bLRIndex].wLEDsRaw;
						fc_CheckChanged = &xLocalTestClass::checkChanged_2Bit;
						fc_SetValue		= &xLocalTestClass::setValue_2Bit;
					} break;
					case 'c': {
						pMemory = &memory_System.bSlidingSwitch_Left;
						fc_CheckChanged = &xLocalTestClass::checkChanged_Value;
						fc_SetValue		= &xLocalTestClass::setValue_Value;
					} break;
				} // end switch level2
				
				
				// do things with function pointers
				// remember Original Operation
				byte bOperationOld = bOperation;
				// do as long as Operation changes:
				do {
					bOperationOld = bOperation;
					switch (bOperation) {
						case hdOperation_GET: {
							Serial.println(F("hdOperation_GET"));
							*pValue = *pMemory;
						}
						case hdOperation_RECEIVE: {
							Serial.println(F("hdOperation_RECEIVE"));
							//*pValue = getValue_Byte(caValue);
							*pValue = wValueReceive;
							bOperation = hdOperation_SETINT;
						} break;
						case hdOperation_SETINT:
						case hdOperation_SET: {
							Serial.println(F("hdOperation_SET(INT)"));
							// check if value changes:
							if ((*this.*fc_CheckChanged)(pMemory, pValue, bBitIndex) ) {
								// set value
								(*this.*fc_SetValue)(pMemory, pValue, bBitIndex);
								// check if need to transmit new value
								if (bOperation == hdOperation_SET) {
									bOperation = hdOperation_TRANSMIT;
								} else {
									// callback 'ValueChanged'
								}
							}
						} break;
						case hdOperation_TRANSMIT:{
							Serial.println(F("hdOperation_TRANSMIT"));
							Serial.print(F(" MemoryValue:"));
							Serial.println(*pMemory);
							//printValue_Byte(streamCommunication, *pMemory);
						} break;
					} // end switch bOperation
				} while (bOperationOld != bOperation);
				
				return 1;
			};
			
			
}; // end class slight_Test


xLocalTestClass myLocalTestObject(42);


void setup() {
	Serial.println(F("test_functionpointer.ino"));
	
	myLocalTestObject.begin();
	
	word wValue = 0;
	word *pValue = &wValue;
	
	//byte test(byte bOperation, byte bSwitchMemory, byte bBitIndex, word *pValue, wValueReceive)
	Serial.println(F("\t SET: pValue = &500;"));
	*pValue = 500;
	myLocalTestObject.test(xLocalTestClass::hdOperation_SET,		'a', 1, pValue, 0);
	
	Serial.println(F("\t TRANSMIT:"));
	myLocalTestObject.test(xLocalTestClass::hdOperation_TRANSMIT,	'a', 1, pValue, 0);
	
	Serial.println(F("\t GET:"));
	myLocalTestObject.test(xLocalTestClass::hdOperation_GET,		'a', 1, pValue, 0);
	Serial.print(F("\t return value:"));
	Serial.println(*pValue);
	
	Serial.println(F("\t RECEIVE:"));
	myLocalTestObject.test(xLocalTestClass::hdOperation_RECEIVE,	'a', 1, pValue, 0);
}

void loop() {
	1;
}

Errors:

test_functionpointer.ino: In member function 'byte xLocalTestClass::test(byte, char, byte, word*, word)':
test_functionpointer:183: error: cannot convert 'bool*' to 'word*' in assignment

this is the problem with the different datatypes...

i think i could do something like this with an void* pointer??
or is there some from ground up other way to do something like this?

sunny greetings

stefan

test_functionpointer:183: error: cannot convert 'bool*' to 'word*' in assignment

Would it make more sense if the message said:

test_functionpointer:183: error: will not convert 'bool*' to 'word*' in assignment

The conversion can be done, but you must do it explicitly. The compiler will not do it implicitly.

word is a data type that Microsoft invented. Unless you are running code on a Microsoft OS, don't use word.

I'm not sure exactly what the code is trying to do, but I changed the line:

        pMemory = &memory_System.bSlidingSwitch_Left;

to

       pMemory = (word *) &memory_System.bSlidingSwitch_Left;

and the code compiles. The error is caused because, without the cast, there is a type mismatch between the two data types. Many compilers don't complain on a "silent cast", because you're attempting to place a smaller data type (boolean) into a larger data type (word). That is, you're pouring one byte of information into a two-byte bucket. Any reasonably good compiler will complain on assignment in the other direction (pour two bytes of information into a one-byte bucket, since that runs the risk of losing information).

Thanks for your quick responses.

@PaulS:
i don't know that - what would be the thing you prefere:

unsigned int
// or 
uint16_t

i have read that the 'uint16_t' thing is the cleanest way - just harder to get for beginners?

@econjack:
thanks for the practical example :wink:
i tried around with some 'carst constructions' but they don't woked..
i used exactly this - big to small concept.
all functions and variables for handling data were the bigest typ. so i can use this to store smaller types in them...

if - in future i want to add some other datatypes to my system - (eventually bigger than the 2byte)
i think the best way is to use a void* pointer?!
i think i than have keep track on the used size the pointer is pointing to..
could this work? or do you have some other ideas to handle such a thing?
i some way i am unsure if the approach i try is the right thing for the task..

sunny greetings
stefan

i have read that the 'uint16_t' thing is the cleanest way - just harder to get for beginners?

Why? The name describes the type exactly AND clearly indicates the size.

But, unsigned int is fine, too.

void pointers pose certain problems and are usually used to get around the compiler complaining about a missing type specifier for a pointer. If you want to write a "generic" function that returns a pointer, you can do something like this:

void setup() { 
 //Initialize serial and wait for port to open:
  Serial.begin(115200); 

} 

void *myGeneric() {
   // Do something
}

void loop() { 
  int *ptr;
  long *lPtr;
  
  ptr = (int *) myGeneric();
  lPtr = (long *) myGeneric();

}

The casts on the return value is required so that the scalar associated with the data type of the pointer is known.

Thanks for your explanation of the void* pointer thing :slight_smile:

i have used this and managed to get the most things working.

know i have a conceptual problem -

i have a mapping to parse the value from an incoming string (char array)
for this function i also want to use a function pointer -
its the same way as for the other function pointer...
but at the moment this functions are returning different data types -
so the compiler complains about he can not assign the pointer - ok
thats clear -
but how to handle this?
i 'think' i need a return type that is different for each function...
i tested - i cant return a pointer to an internal variable of the called function - thats also clear -
if the function exits the pointer losses his destination.
i could use a in the calling function declared variable and give the parsing function a pointer to this-
so it could write the parsed value to this outside location..
but for this i have to declare a variable - and it needs a type -
so at the moment i can use the biggest used type (for me currently uint16_t)

here is the interessting part of the code...
(the complete test_functionpointer.ino is in the attachment (otherwise the forum message would be to big..)

//.....

// parse
typedef uint16_t (xLocalTestClass::*tCbfunc_parseValue) (char *caValue);
bool _parseValue_Bool(char *caValue) {
	return (bool) atoi(caValue);
};

//....

uint16_t _parseValue_UIntBinary(char *caValue) {
	uint16_t wResult = 0;
	if (strlen(caValue) == 16) {
		byte bCharIndex = 0;
		for (unsigned int mask = 0b1000000000000000; mask; mask >>= 1) {
			// check if this bit is set
			if (caValue[bCharIndex] == '1') {
				// set Bit
				wResult |= mask;
			} // else its 0 so do nothing.
			bCharIndex = bCharIndex + 1;
		}
	}
	return wResult;
};

//.....

// test 
byte test(byte bOperation, char cSwitchMemory, byte bBitIndex, void *pValue, char *caValue) {
	
	// default to left
	byte bLRIndex = 0;
	if ( 1 != 1) {
		// set to right
		bLRIndex = 1;
	}
	
	// pointer to Memory Value
	void *pMemory = NULL;
	
	
	// function pointers
	tCbfunc_CheckChanged fc_CheckChanged = NULL;
	tCbfunc_parseValue fc_parseValue = NULL;
	// tCbfunc_printValue fc_printValue = NULL;
	tCbfunc_setValue fc_setValue = NULL;
	
	switch (cSwitchMemory) {
		case 'a': {
			pMemory = &memory_LeftRight[bLRIndex].wRotaryEncoder_position;
			fc_CheckChanged	= &xLocalTestClass::_checkChanged_UInt;
			fc_setValue		= &xLocalTestClass::_setValue_UInt;
			fc_parseValue	= &xLocalTestClass::_parseValue_UInt;
		} break;
		case 'b': {
			pMemory = &memory_LeftRight[bLRIndex].wLEDsRaw;
			fc_CheckChanged = &xLocalTestClass::_checkChanged_2Bit;
			fc_setValue		= &xLocalTestClass::_setValue_2Bit;
			fc_parseValue	= &xLocalTestClass::_parseValue_UIntBinary;
		} break;
		case 'c': {
			pMemory = &memory_System.bSlidingSwitch_Left;
			fc_CheckChanged = &xLocalTestClass::_checkChanged_UInt;
			fc_setValue		= &xLocalTestClass::_setValue_Bool;
			fc_parseValue	= &xLocalTestClass::_parseValue_Bool;
		} break;
	} // end switch level2
	
	
	// do things with function pointers
	// remember Original Operation
	byte bOperationOld = bOperation;
	// do as long as Operation changes:
	do {
		bOperationOld = bOperation;
		switch (bOperation) {
			case hdOperation_GET: {
				Serial.println(F("hdOperation_GET"));
				*(uint16_t *) pValue = *(uint16_t *) pMemory;
			}
			case hdOperation_RECEIVE: {
				Serial.println(F("hdOperation_RECEIVE"));
				*pValue = (*this.*fc_parseValue)(caValue);
				bOperation = hdOperation_SETINT;
			} break;
			case hdOperation_SETINT:
			case hdOperation_SET: {
				Serial.println(F("hdOperation_SET(INT)"));
				// check if value changes:
				if ((*this.*fc_CheckChanged)(pMemory, pValue, bBitIndex) ) {
					// set value
					(*this.*fc_setValue)(pMemory, pValue, bBitIndex);
					// check if need to transmit new value
					if (bOperation == hdOperation_SET) {
						bOperation = hdOperation_TRANSMIT;
					} else {
						// callback 'ValueChanged'
					}
				}
			} break;
			case hdOperation_TRANSMIT:{
				Serial.println(F("hdOperation_TRANSMIT"));
				Serial.print(F(" MemoryValue:"));
				Serial.println((*(uint16_t *)pMemory));
				//printValue_Byte(streamCommunication, pMemory);
			} break;
		} // end switch bOperation
	} while (bOperationOld != bOperation);
	
	return 1;
};

i don't know if the described would be a 'nice' solution -
is there a other solution / way / concept for this problem out there?

i have appended my main sketch -
its a library so put it in your sketchbook/libraries/ folder. it contains a example.
this is the sketch for testing and developing all functionality..
short description what is does:
read the serial port.
if it receives a complete line (checks for line endings and prevents from internal buffer overflows...)
parse this line.
--> use the handleData function in 'hdOperation_RECEIVE' mode.

this handleData function is the main construct to interface the internal representation of the system data.
and my idea / goal ist to only have one interface for internal get / set and external transmit / receive operations

i hope someone has a tip / idea about what ways to go with this... :slight_smile:

sunny greetings stefan

slight_2ChComLink__20140411_1127.zip (15.1 KB)

test_functionpointer.ino (9.83 KB)

Perhaps you could pass in a pointer to a union and, upon return from the function, extract the value you need that the function placed in the union.