untyped pointers.

I have a need to return a pointer for a unknown data type.

This example code shows a global data structure with values, a helper routine to retrieve the values and a menu display routine
that calls the helper routine to fill in the fields of the menu. the data type is know by the field.

this is the field definition:
[%03D1] : the square brackets identify the field.
the % marks a variable
the 0 means zero left fill.
the 3 is field width
the D marks it as a decimal field
the 1 is field number 1 for helper routines.

typedef struct {
   int a;
   char st[80];
   uint8_t d,e;
} CFGS;

CFGS cfg;

Pointer data_handler( int field_id){   // the compiler dies complaining about unknown named type (Pointer) I substitute void * 
                                                      // but the results are not what I expect

   switch(field_id) {
      case 1: return &cfg.a;
         break;
      case 2: return &cfg.st;
         break;
      case 3: return &cfg.d;
         break;
      case 4: return &cfg.e;
        break;
     default : ;
}

void setup(){
int i;
do{
   i = disp_menu("menu text [%03D1] name [%8A2]",data_handler);
}while(I!=0);
}

// example data structures,  that support the menu fields

union FLDbits {
  struct {
    unsigned char SPACEFill : 1;
    unsigned char ZEROFill : 1;
    unsigned char : 6;};
   unsigned char value;
};

typedef struct {
   int fieldid;
   int fieldtype;
   int location;
   FLDbits f_opts;
} FLDS;

FLDS flds[10];  

// the actual routine that needs the untyped pointer function.


int disp_menu(const char st[],Pointer (*dh)(int)){

// create menu structure ... missing code

for(int i=0;i<fldcount;i++){
   switch(flds[i].fieldtype){
      case 1: int * i = dh(flds[i].fieldid);
          // fill in field with integer value
         break;
     case 2: char * ch[] = dh(flds[i].fieldid);
          // fill in field with character value
         break;

// the rest of the code

Does anyone understand what I am asking? I am new to the Arduino, I mainly use Delphi. C++ throws me. and the Restrictions of Arduino confuse me even more.

Chuck Todd

A void pointer is anonymous.
You can cast it to be a pointer of any other type.

Why do you want to do this? The design looks shaky to me.

You have various types in the struct. Making a function to return different types seems a dodgy idea.

You seem to be trying to force C to do something it is not designed for. You could do it with a lot of casting and stuff, but I just don't like the look of it.

One idea: Make a class with a "getter" and "setter" class function.

The purpose is to support menu structures in an external EEPROM. All of the Menu defining info is in a EEProm instead of using the Arduino's limited SRAM.

I only need to code the Helper routines to save, retrieve the variables values.

I have used a similar structure for years.

My desire is to create a library that will process the Menu structure with minimal coding in the main loop.

What is the correct format for untyped pointers?

How do I correctly code this:

Pointer helper(int i);

Thanks

chuck

AWOL:
A void pointer is anonymous.
You can cast it to be a pointer of any other type.

Can you give me an working example for this code?

int x =5;
char b[] ="hello";

Pointer helper(int i){
switch (i){
  case 1 : return &x;
  case 2 : return &b;
  default :;
  }
}

Thanks
Chuck

void* helper(int i)

But, as Nick pointed out, it probably is a bad way to go.

A couple issues:

  1. data_handler needs to be declared "void *" so that it can return a pointer to void.
  2. When you use that void pointer, you will need to know its type and recast it back.

Here would be an example:

void * data_handler(...) {
...}

void * myptr;
  myptr=data_hander(1); //myptr points to cfg.a (int)
  value = * (int *) myptr;

  myptr=data_handler(2); //myptr points to cfg.st (unsigned char *)
  printf("cfg.st=%s", (unsigned char *) myptr);

  myptr=data_handler(3); //myptr points to cfg.d (uint8_t)
  printf("cfg.d=%4d\n", * (uint8_t *) myptr);

Just some example.

If you go down this route you are totally defeating the compiler's checking of types. Now you may say "but I want to do that!" but this is basically bad design. You just have to decide one day to turn an int into a long because there is a requirement for a longer number, and things start failing in mysterious ways.

As I said, you would be better off with a proper class, and "get" and "set" functions, all with the correct type (a template may well help to write those).

Thank you. That is just what I wanted. :grin:
I realize that when I use untyped pointers I am walking a tight rope.

My actual routines have alittle more error checking it them, the actual call back support routine looks like this:
(now with your corrected syntax)

void * helper (int fld, int fldtype);

Basically I am doing run time data type conversion.

dhenry:
A couple issues:

  1. data_handler needs to be declared "void *" so that it can return a pointer to void.
  2. When you use that void pointer, you will need to know its type and recast it back.

Here would be an example:

void * data_handler(...) {

...}

void * myptr;
 myptr=data_hander(1); //myptr points to cfg.a (int)
 value = * (int *) myptr;

myptr=data_handler(2); //myptr points to cfg.st (unsigned char *)
 printf("cfg.st=%s", (unsigned char *) myptr);

myptr=data_handler(3); //myptr points to cfg.d (uint8_t)
 printf("cfg.d=%4d\n", * (uint8_t *) myptr);




Just some example.

void pointers are actually fairly simple to undestand, as long as you remember two things:

  1. Before you use them, you will need to recast them back to the appropriate type.
  2. Once they are recast back to the right types, you will need to let your code know that they are pointers.

Take this for example:

 value = * (int *) myptr;

"(int ) myptr" recasts myptr to a pointer to an int. The "" in front of "(int *) myptr" tells your code that the recast type is a pointer.

You can think of it as two statements:

  int * tmp;

  tmp  = (int *) myptr; //tmp now points to the same content as myptr does;
  value = *tmp; //value takes on the content pointed to by tmp

The "*" in front of "(int *) myptr" tells your code that the recast type is a pointer.

No, the cast tells you it is a pointer, the initial * dereferences it.

What I would do if I were the OP is to store menu texts in PROGMEM. There's plenty of space for a moderate project. If that is exhausted, try EEPROM. But in both cases, try to create an SRAM buffer so everything can be loaded to and done in SRAM regardless where the text comes from, just in case next time you want to pull text from thin-air storage devices :wink:

Here is an example of doing it with the type-casts moved into a single spot for safety. This lets you have a generic menu of different types.

// offset of an element in a structure
#define offsetof(st,arg)   (size_t) &(((st *)NULL)->arg)

// offset and size of a menu item in our structure
#define OFF(st,arg) offsetof (st, arg) , sizeof (((st *)NULL)->arg)

// number of items in an array
#define NUMITEMS(arg) ((size_t) (sizeof (arg) / sizeof (arg [0])))

// flags for menu
const byte FLAG_STRING = 1;
const byte FLAGB = 2;  // whatever

typedef struct
  {
  byte    which;      // menu number
  int     iOffset;    // offset in struct
  int     iLength;    // length of field (ie. 1, 2, 4 bytes)
  int     iFlags;     // other stuff (eg. is field a string)
  }  tMenuOption;

// prototype  
long getOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase);
  
long getOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase)
  {
  // get variable address
  char * p = pBase + optionsTable [iItem].iOffset;
  long Value;

  switch (optionsTable [iItem].iLength)
    {
    case 1:
      Value = * (byte *) p;
      break;

    case 2:
      Value =  * (unsigned int *) p;
      break;

    case 4:
      Value =  * (unsigned long *) p;
      break;
    } // end of switch

  return Value;
  }  // end of getOptionItem

// prototype
char * getStringOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase);

char * getStringOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase)
  {
  return (char *) (pBase + optionsTable [iItem].iOffset);
  }  // end of getStringOptionItem


// prototype
void setOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase, long Value);

void setOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase, long Value)
  {
  // get variable address
  char * p = pBase + optionsTable [iItem].iOffset;

  switch (optionsTable [iItem].iLength)
    {

    case 1:
      * p = (byte) Value;
      break;

    case 2:
      * (unsigned int *) p = (unsigned int) Value;
      break;

    case 4:
      * (unsigned long *) p = Value;
      break;
    } // end of switch
  } // end of setOptionItem

// prototype
void setStringOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase, char * sValue);

void setStringOptionItem (const int iItem, tMenuOption optionsTable [], char * pBase, char * sValue)
  {
  strcpy (pBase + optionsTable [iItem].iOffset, sValue);
  } // end of setStringOptionItem


// test code ---------

typedef struct {
   int a;
   char st[80];
   uint8_t d,e;
} CFGS;

CFGS cfg = { 42, "foo", 100, 200 };

tMenuOption menuA [] = {
     { 1, OFF (CFGS, a),  0 },  
     { 2, OFF (CFGS, st), FLAG_STRING },  
     { 3, OFF (CFGS, d),  0 },  
     { 4, OFF (CFGS, e),  0 },  
};


void showValues ()
  {
  for (int i = 0; i < NUMITEMS (menuA); i++)
    {
    Serial.print ("i = ");
    Serial.print (i);
    Serial.print (", menu item = ");
    Serial.print (menuA [i].which);
    Serial.print (", value = ");

    if (menuA [i].iFlags == FLAG_STRING)
      {
      char * pVal = getStringOptionItem (i, menuA, (char *) &cfg); 
      Serial.println (pVal);  
      }
    else
      {
      long val = getOptionItem (i, menuA, (char *) &cfg);  
      Serial.println (val);
      }
    }
    
  }
  
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  showValues ();
  
  setOptionItem (0, menuA, (char *) &cfg, 55);
  setStringOptionItem (1, menuA, (char *) &cfg, "bar");
  setOptionItem (2, menuA, (char *) &cfg, 222);
  setOptionItem (3, menuA, (char *) &cfg, 77);
  
  Serial.println ("After change ...");
  showValues ();
  
  }  // end of setup
  
void loop ()
  {
    
  }  // end of loop

Test output:

i = 0, menu item = 1, value = 42
i = 1, menu item = 2, value = foo
i = 2, menu item = 3, value = 100
i = 3, menu item = 4, value = 200
After change ...
i = 0, menu item = 1, value = 55
i = 1, menu item = 2, value = bar
i = 2, menu item = 3, value = 222
i = 3, menu item = 4, value = 77

I have two generic "getter" and "setter" functions, one for strings, one for numbers. The numbers are communicated as unsigned longs, but retrieved/stored in the original format.

The above requires 1.0.2 of the IDE, because of the way the function prototypes are done.