Classes and Objects in c++

I’m trying to do something beyond my C++ knowledge.

I want to put objects of a class in flash with lots and lots of data. So I made a class:

class junk
{
	uint16_t index[] ;
	uint8_t  bits[] ;
public:
	int Height ;
	int Ascent ;

};

Eventually there will be some methods too.

Index and bits will hold lots of data this dummy isn’t going to type in.

To make an object of this type, I tried to:

const uint16_t TrashIndex[] = {1,2,3,4};
const uint8_t TrashBits[] = {5,6,7,8};

const junk Trash = 
{
	(uint16_t *) TrashIndex ,
	(uint8_t *) TrashBits ,
	9, 10 
};

The compiler says I don’t know what I’m doing. This is no surprise:

error: could not convert '{((uint16_t*)((const uint16_t*)(& TrashIndex))), ((uint8_t*)((const uint8_t*)(& TrashBits))), 9, 10}' from '<brace-enclosed initializer list>' to 'const junk'
 };
 ^

The hat (^) seems to be pointing at the closing brace of trash.

I’ve done this sort of thing before with just a huge array of bytes, but that is VERY easy to break and hard to debug, so I’m trying to get smarter.

That won't work for non-static fields of a class.
Even if you mark an object as const the actual construction and initialisation will not happen at compile time and hence the data segment of the object won't be placed in flash.

For that to work you'd need to overload an assignment/copy operator that takes your data, constructs an object, copies the individual fields into that object and then returns a reference to that object to replace the default object.

However, for a fresh construction of an object you should rather have a dedicated constructor.

Hmm. Maybe I’ll have to stick with structs.

What exactly do you want to do with the struct? Store data in it? Save and load it to EEPROM?

You could make a struct like this:

struct MyObject {
  uint8_t version;
  float field1;
  uint16_t field2;
  char name[10];
};
MyObject myObj = { 0, 12.34f, 25, "Test!" };

https://docs.particle.io/reference/device-os/firmware/photon/#put-

That should work - as long you mean POD-structs (plain-old-data) and not struct in the extended meaning of C++ struct (where class and struct are basically the same just with the difference in default access level).

I want to store kilobytes of data in flash for the program to refer to in operation.

I’ve been just using

const uint8_t stuff[] = {
  1, 6 4, 323, bla bla bla
};

But poorly structured this way it’s easy to goof up and hard to debug, so I want to use more computer science.

If this data is less than 3K bytes why not use a struct in Retained RAM? This would be a bit easier than some mind bending/advanced C++ overload/copy operator.

Following is an example of creating a struct with predefined data in retained RAM.

struct Locker {
byte state;
char reference[5];
};
//create 160 objects and initialise them with default reference
retained Locker lockers[] ={{0,{"0001"}},{0,{"0002"}},{0,{"0003"}},{0,{"0004"}},{0,{"0005"}},{0,{"0006"}},{0,{"0007"}},{0,{"0008"}},{0,{"0009"}},{0,{"0010"}},
                            {0,{"0011"}},{0,{"0012"}},{0,{"0013"}},{0,{"0014"}},{0,{"0015"}},{0,{"0016"}},{0,{"0017"}},{0,{"0018"}},{0,{"0019"}},{0,{"0020"}},
                            {0,{"0021"}},{0,{"0022"}},{0,{"0023"}},{0,{"0024"}},{0,{"0025"}},{0,{"0026"}},{0,{"0027"}},{0,{"0028"}},{0,{"0029"}},{0,{"0030"}},
                            {0,{"0031"}},{0,{"0032"}},{0,{"0033"}},{0,{"0034"}},{0,{"0035"}},{0,{"0036"}},{0,{"0037"}},{0,{"0038"}},{0,{"0039"}},{0,{"0040"}},
                            {0,{"0041"}},{0,{"0042"}},{0,{"0043"}},{0,{"0044"}},{0,{"0045"}},{0,{"0046"}},{0,{"0047"}},{0,{"0048"}},{0,{"0049"}},{0,{"0050"}},
                            {0,{"0051"}},{0,{"0052"}},{0,{"0053"}},{0,{"0054"}},{0,{"0055"}},{0,{"0056"}},{0,{"0057"}},{0,{"0058"}},{0,{"0059"}},{0,{"0060"}},
                            {0,{"0061"}},{0,{"0062"}},{0,{"0063"}},{0,{"0064"}},{0,{"0065"}},{0,{"0066"}},{0,{"0067"}},{0,{"0068"}},{0,{"0069"}},{0,{"0070"}},
                            {0,{"0071"}},{0,{"0072"}},{0,{"0073"}},{0,{"0074"}},{0,{"0075"}},{0,{"0076"}},{0,{"0077"}},{0,{"0078"}},{0,{"0079"}},{0,{"0080"}},
                            {0,{"0081"}},{0,{"0082"}},{0,{"0083"}},{0,{"0084"}},{0,{"0085"}},{0,{"0086"}},{0,{"0087"}},{0,{"0088"}},{0,{"0089"}},{0,{"0090"}},
                            {0,{"0091"}},{0,{"0092"}},{0,{"0093"}},{0,{"0094"}},{0,{"0095"}},{0,{"0096"}},{0,{"0097"}},{0,{"0098"}},{0,{"0099"}},{0,{"0100"}},
                            {0,{"0101"}},{0,{"0102"}},{0,{"0103"}},{0,{"0104"}},{0,{"0105"}},{0,{"0106"}},{0,{"0107"}},{0,{"0108"}},{0,{"0109"}},{0,{"0110"}},
                            {0,{"0111"}},{0,{"0112"}},{0,{"0113"}},{0,{"0114"}},{0,{"0115"}},{0,{"0116"}},{0,{"0117"}},{0,{"0118"}},{0,{"0119"}},{0,{"0120"}},
                            {0,{"0121"}},{0,{"0122"}},{0,{"0123"}},{0,{"0124"}},{0,{"0125"}},{0,{"0126"}},{0,{"0127"}},{0,{"0128"}},{0,{"0129"}},{0,{"0130"}},
                            {0,{"0131"}},{0,{"0132"}},{0,{"0133"}},{0,{"0134"}},{0,{"0135"}},{0,{"0136"}},{0,{"0137"}},{0,{"0138"}},{0,{"0139"}},{0,{"0140"}},
                            {0,{"0141"}},{0,{"0142"}},{0,{"0143"}},{0,{"0144"}},{0,{"0145"}},{0,{"0146"}},{0,{"0147"}},{0,{"0148"}},{0,{"0149"}},{0,{"0150"}},
                            {0,{"0151"}},{0,{"0152"}},{0,{"0153"}},{0,{"0154"}},{0,{"0155"}},{0,{"0156"}},{0,{"0157"}},{0,{"0158"}},{0,{"0159"}},{0,{"0160"}}};

What you are using there is considered a POD-struct and that can live in flash.

In fact the way you wrote it there it’ll take up space in both flash and retained RAM since the initialisation values have to be persisted in non-volatile memory in order to populate the RAM on startup.

3 Likes

Absolutely correct - a quick cut and paste to illustrate a struct with initialised references.
I would in code write something to initialise the plain old data types as required but this was from a testing scaffold.

1 Like

I was thinking of the way the adafruit libraries store fonts, which are similar in size. They define two types:

/// Font data stored PER GLYPH
typedef struct {
	uint16_t bitmapOffset;     ///< Pointer into GFXfont->bitmap
	uint8_t  width;            ///< Bitmap dimensions in pixels
        uint8_t  height;           ///< Bitmap dimensions in pixels
	uint8_t  xAdvance;         ///< Distance to advance cursor (x axis)
	int8_t   xOffset;          ///< X dist from cursor pos to UL corner
        int8_t   yOffset;          ///< Y dist from cursor pos to UL corner
} GFXglyph;

/// Data stored for FONT AS A WHOLE
typedef struct { 
	uint8_t  *bitmap;      ///< Glyph bitmaps, concatenated
	GFXglyph *glyph;       ///< Glyph array
	uint8_t   first;       ///< ASCII extents (first char)
        uint8_t   last;        ///< ASCII extents (last char)
	uint8_t   yAdvance;    ///< Newline distance (y axis)
} GFXfont;

Then they make const variables of these types:

const uint8_t FreeMono12pt7bBitmaps[] PROGMEM = {
  0x49, 0x24, 0x92, 0x48, 0x01, 0xF8, 0xE7, 0xE7, 0x67, 0x42, 0x42, 0x42,
0x42, 0x09, 0x02, 0x41, 0x10, 0x44, 0x11, 0x1F, 0xF1, 0x10, 0x4C, 0x12,

   ... bla bla bla ...
   };

const GFXglyph FreeMono12pt7bGlyphs[] PROGMEM = {
  {     0,   0,   0,  14,    0,    1 },   // 0x20 ' '
{ 0, 3, 15, 14, 6, -14 }, // 0x21 '!'
... bla bla bla ...
   };

And then they build the structure:

const GFXfont FreeMono12pt7b PROGMEM = {
  (uint8_t  *)FreeMono12pt7bBitmaps,
  (GFXglyph *)FreeMono12pt7bGlyphs,
0x20, 0x7E, 24 };

I think I should be able to adapt this technique.

(I saw this whilst I was playing with the ILI9341 code)

The above Adafruit reference code would work just fine for you. If you do in fact want to scope everything as a class for some other reason, you should be able to keep all that data in flash by creating a class with static members (as ScruffR alluded to) as below:

class junk
{
	static const uint16_t index[] = {1,2,3,4};
	static const uint8_t  bits[] = {5,6,7,8};
public:
	int Height ;
	int Ascent ;
};

OR by doing what you tried to do initially, but just properly initializing the pointers in your class constructor.

const uint16_t index[] = {1,2,3,4};
const uint8_t  bits[] = {5,6,7,8};

class junk
{
	uint16_t _index*;
	uint8_t  _bits*;
public:
	junk(int start_height = 0, int start_ascent = 0) { _index =  index; _bits = bits; Height = start_height; Ascent = start_ascent; }
	~junk() {}
	int Height ;
	int Ascent ;
};

In my opinion I think the first option is best and most adherent to c++ best practices if it’s really only storing data. If you want to add methods to operate on that data abstracted within a class, the second is cleaner and more adherent.

EDIT: whoops forgot to delete the assignment in the second example

Ok. Here’s what I did and it seems to be working:

typedef struct {
	uint16_t bitmapOffset;     ///< Pointer into GFXfont->bitmap
	uint8_t  height ;
	int16_t   a ;
	int16_t   b ;
} trash ;;

typedef struct { 
	uint8_t  *bitmap;     
	glyph 	 *trash;       

	uint8_t   Height;   
	uint8_t	  Ascent ;
} junk;

And I built the data in flash like so:

const trash somestuff[] = {
{     0,   7,  30,     0,     4 }, 
{ 27, 12, 30, 2, 2 },
   bla bla bla
{ 6528, 8, 30, 0, 7}
};

const uint8_t blobofbytes[] = {
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   bla bla bla (most rows aren't all zeros, but I bet I could be clever and reduce those that are.)
 0x0e, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0xf0
};

const junk aloadofstuff= {
  (uint8_t *) blobofbytes,
  (glyph *) sumstuff,
  30, 23
};

And I can pass aloadofstuff as a parameter and access all the works inside.

1 Like

It also was a lot easier to get the code correct than the massive blob of unstructured bytes was.

1 Like

'Tis intuitively obvious to the most casual observer that…

The code I posted won’t make 'cause I put 1 fewer member in the trash structure than is in the const data.