I have a structure like this
struct foo {
string str1;
uint16_t int1
string str2;
uint32_t int2;
string str3;
};
strings str1, str2 , str3 are of fixed length of 12 bytes, 3 bytes,etc. left padded with spaces.
I have a function
void func(const byte* data, const size_t len) which is supposed to convert the byte * data to structure foo. len is length of data.What are the ways in which I can do this?
Again the data is const pointer of byte type and will not have null characters in between to distinguish different members.
Should I use character array instead of string for str1, str2, str3?
Easiest (but most errorprone) way is to just reinterpret_cast / std::memcpy if the strings have fixed length:
// no padding
#pragma pack(push, 1)
struct foo {
char str1[12];
uint16_t int1;
char str2[3];
uint32_t int2;
char str3[4];
};
#pragma pack(pop)
void func(const byte* data, const size_t len) {
assert(len == sizeof(foo));
// non owning
const foo* reinterpreted = reinterpret_cast<const foo*>(data);
// owning
foo reinterpreted_val = *reinterpret_cast<const foo*>(data);
foo copied;
memcpy(&copied, data, len);
}
Notes:
Make sure that you're allowed to use reinterpret_cast
https://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing
if you'd try to use strlen or another string operation on any of the strings you most likely will get UB, since the strings are not null terminated.
Slightly better approach:
struct foo {
char str1[13];
uint16_t int1;
char str2[4];
uint32_t int2;
char str3[5];
};
void func(const char* data, const size_t len) {
foo f;
memcpy(f.str1, data, 12);
f.str1[12] = '\0';
data+=12;
memcpy(&f.int1, data, sizeof(uint16_t));
data+=sizeof(uint16_t);
memcpy(f.str2, data, 3);
f.str2[3] = '\0';
data+=3;
memcpy(&f.int2, data, sizeof(uint32_t));
data+=sizeof(uint32_t);
memcpy(f.str3, data, 4);
f.str3[4] = '\0';
data+=4;
}
Notes:
You could combine both approaches to get rid of the pointer arithmetic. That would also account for any padding in your struct you might have.
I think the easiest way to do this is to change the string inside the structure
to the type of char. Then you can easily copy the objects of this
structure according to its size.
you will have to somehow deal with the byte order on machines with different byte
order
struct foo {
char str1[12];
uint16_t int1;
char str2[3];
uint32_t int2;
char str3[5];
};
byte* Encode(foo* p, int Size) {
int FullSize = Size * sizeof(foo);
byte* Returner = new byte[FullSize];
memcpy_s(Returner, FullSize, p, FullSize);
return Returner;
}
foo * func(const byte* data, const size_t len) {
int ArrSize = len/sizeof(foo);
if (!ArrSize || (ArrSize* sizeof(foo)!= len))
return nullptr;
foo* Returner = new foo[ArrSize];
memcpy_s(Returner, len, data, len);
return Returner;
}
int main()
{
const size_t ArrSize = 3;
foo Test[ArrSize] = { {"Test1",1000,"TT",2000,"cccc"},{"Test2",1001,"YY",2001,"vvvv"},{"Test1",1002,"UU",2002,"bbbb"}};
foo* Test1 = nullptr;
byte* Data = Encode(Test, ArrSize);
Test1 = func(Data, ArrSize * sizeof(foo));
if (Test1 == nullptr) {
std::cout << "Error extracting data!" << std::endl;
delete [] Data;
return -1;
}
std::cout << Test1[0].str1 << " " << Test1[1].str1 << " " << Test1[2].str3 << std::endl;
delete [] Data;
delete[] Test1;
return 0;
}
output
Test1 Test2 bbbb
This is a program for using fonts in MCUs. I have 4 files. 'zar.h' and 'zar.cpp' contains data of one font. 'font.h' and 'font.cpp' contains pointers and functions for set fonts dynamically. There is a lot of problems with pointers. When access elements of "XB_Zar_name" array, by "uni_name[i]" pointer, MCU dos reset's. Another problem is counting the strings in "XB_Zar_name" string array. There are definitely other errors that will appear after fixing these errors. Pleas help me.
zar.h
/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern char XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern String XB_Zar_name[];
zar.cpp
/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
char XB_Zar_width[]= {2,2,2,2,2};
const unsigned char* XB_Zar_addr[] = {XB_Zar_118,XB_Zar_119,XB_Zar_120,XB_Zar_121,XB_Zar_122};
String XB_Zar_name[] = {"۰","۱","۲","۳","۴","۵"};
font.h
#pragma once
#include "zar.h"
extern unsigned char *char_addr;
extern unsigned int *char_width;
extern String **uni_name;
extern void setFont(byte fontNumber);
extern int getGliphData(String *uniChar, int *index, int *width);
font.cpp
#include "font.h"
unsigned char *char_addr = NULL;
unsigned int *char_width = NULL;
String **uni_name = NULL;
//########################################################
void setFont(byte fontNumber)
{
switch (fontNumber)
{
case 0:
char_addr = (unsigned char *)(XB_Zar_addr);
*uni_name = XB_Zar_name;
char_width = (unsigned int *)XB_Zar_width;
break;
default:
char_addr = (unsigned char *)XB_Zar_addr;
break;
}
}
//########################################################
int getGliphData(String *uniChar, int* index, int* charWidth)
{
Serial.println("uniChar data= "+*uniChar);
Serial.println("uni_name[i]= "+*uni_name[0]);// <---this resets the mcu
int countOfUniChars = sizeof(uni_name) / sizeof(String*);
for (int i = 0; i < countOfUniChars ; i++)
{
if (*uniChar == *uni_name[i])
{
*index = i;
*charWidth = char_width[i];
return i;
}
}
return -1;
}
Inside of getGliphData(), uni_name is just a pointer, so the sizeof() trick you are trying to use to calculate countOfUniChars simply won't work (there are numerous questions on StackOverflow on that very issue).
So, either store the array length into another global variable at the same time you store the array pointer, or else terminate each array with a sentry value (in this case, a blank string) and then loop through the array until you reach the sentry.
Also, there is no need to use a String** pointer when a String* pointer will suffice, as you are iterating through only 1 array of Strings. Were you iterating through an array of pointers to arrays of Strings then String** would make more sense.
Also, another problem I see is that XB_Zar_width[] and XB_Zar_addr[] only have 5 elements in them, but XB_Zar_name[] has 6 elements, and getGliphData() is trying to loop through XB_Zar_name[] using its indexes to access the elements of XB_Zar_width[] and XB_Zar_addr[], which will obviously not work for the 6th element.
That being said, try something more like this instead:
// zar.h
/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern const unsigned int XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern const String XB_Zar_name[];
extern const int XB_Zar_name_len;
//zar.cpp
/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
const unsigned int XB_Zar_width[] = {2, 2, 2, 2, 2, 0}; // TODO: fix the 6th element!
const unsigned char* XB_Zar_addr[] = {XB_Zar_118, XB_Zar_119, XB_Zar_120, XB_Zar_121, XB_Zar_122, NULL}; // TODO: fix the 6th element!
const String XB_Zar_name[] = {"۰", "۱", "۲", "۳", "۴", "۵"}; // TODO: remove the 6th element?
const int XB_Zar_name_len = sizeof(XB_Zar_name)/sizeof(XB_Zar_name[0]);
//font.h
#pragma once
#include "zar.h"
extern const unsigned char **char_addr;
extern const unsigned int *char_width;
extern const String *uni_name;
extern int uni_name_len;
extern void setFont(byte fontNumber);
extern int getGliphData(const String &uniChar, int *index, int *width);
//font.cpp
#include "font.h"
const unsigned char **char_addr = NULL;
const unsigned int *char_width = NULL;
const String *uni_name = NULL;
int uni_name_len = 0;
//########################################################
void setFont(byte fontNumber)
{
switch (fontNumber)
{
case 0:
char_addr = XB_Zar_addr;
char_width = XB_Zar_width;
uni_name = XB_Zar_name;
uni_name_len = XB_Zar_name_len;
break;
default:
char_addr = XB_Zar_addr;
char_width = XB_Zar_width;
break;
}
}
//########################################################
int getGliphData(const String &uniChar, int* index, int* charWidth)
{
Serial.println("uniChar data= " + uniChar);
if (uni_name)
{
for (int i = 0; i < uni_name_len; ++i)
{
Serial.println("uni_name[" + String(i) + "]= " + uni_name[i]);
if (uniChar == uni_name[i])
{
*index = i;
*charWidth = char_width[i];
return i;
}
}
}
return -1;
}
Online Demo
Alternatively:
// zar.h
/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern const unsigned int XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern const String XB_Zar_name[];
//zar.cpp
/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
const unsigned int XB_Zar_width[] = {2, 2, 2, 2, 2, 0}; // TODO: fix the 6th element!
const unsigned char* XB_Zar_addr[] = {XB_Zar_118, XB_Zar_119, XB_Zar_120, XB_Zar_121, XB_Zar_122, NULL}; // TODO: fix the 6th element!
const String XB_Zar_name[] = {"۰", "۱", "۲", "۳", "۴", "۵", ""}; // TODO: remove the 6th element?
//font.h
#pragma once
#include "zar.h"
extern const unsigned char **char_addr;
extern const unsigned int *char_width;
extern const String *uni_name;
extern void setFont(byte fontNumber);
extern int getGliphData(const String &uniChar, int *index, int *width);
//font.cpp
#include "font.h"
const unsigned char **char_addr = NULL;
const unsigned int *char_width = NULL;
const String *uni_name = NULL;
//########################################################
void setFont(byte fontNumber)
{
switch (fontNumber)
{
case 0:
char_addr = XB_Zar_addr;
char_width = XB_Zar_width;
uni_name = XB_Zar_name;
break;
default:
char_addr = XB_Zar_addr;
char_width = XB_Zar_width;
break;
}
}
//########################################################
int getGliphData(const String &uniChar, int* index, int* charWidth)
{
Serial.println("uniChar data= " + uniChar);
if (uni_name)
{
for (int i = 0; uni_name[i].Length() > 0; ++i)
{
Serial.println("uni_name[" + String(i) + "]= " + uni_name[i]);
if (uniChar == uni_name[i])
{
*index = i;
*charWidth = char_width[i];
return i;
}
}
}
return -1;
}
Online Demo
I have completed #RemyLebeau excellent answer for running on ESP32 microcontroller.
// zar.h
/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern const unsigned int XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern const String XB_Zar_name[];
extern const int XB_Zar_name_len;
zar.cpp file:
//zar.cpp
/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
const unsigned int XB_Zar_width[] = {2, 2, 2, 2, 2, 0}; // TODO: fix the 6th element!
const unsigned char* XB_Zar_addr[] = {XB_Zar_118, XB_Zar_119, XB_Zar_120, XB_Zar_121, XB_Zar_122, NULL}; // TODO: fix the 6th element!
const String XB_Zar_name[] = {"۰", "۱", "۲", "۳", "۴", "۵"}; // TODO: remove the 6th element?
const int XB_Zar_name_len = sizeof(XB_Zar_name)/sizeof(XB_Zar_name[0]);
font.h file:
//font.h
#pragma once
#include "zar.h"
extern const unsigned char **char_addr;
extern const unsigned int *char_width;
extern const String *uni_name;
extern void setFont(byte fontNumber);
extern int getGliphData(const String &uniChar, int *index, int *width);
font.cpp file:
//font.cpp
#include "font.h"
const unsigned char **char_addr = NULL;
const unsigned int *char_width = NULL;
const String *uni_name = NULL;
//########################################################
void setFont(byte fontNumber)
{
switch (fontNumber)
{
case 0:
char_addr = (unsigned char *)pgm_read_ptr(XB_Zar_addr[0]); // use pgm_read_ptr() becuse there is PROGMEM space in flash memory
char_addr = XB_Zar_addr;
char_width = XB_Zar_width;
uni_name = XB_Zar_name;
break;
default:
char_addr = (unsigned char *)pgm_read_ptr(XB_Zar_addr[0]); // use pgm_read_ptr() becuse there is PROGMEM space in flash memory
char_addr = XB_Zar_addr;
char_width = XB_Zar_width;
uni_name = XB_Zar_name;
break;
}
//Now. char_addr points to flash space and no need to use pgm_read_word_near()
// or using pgm_read_byte_near(). Simply use "*(char_addr)" or "*(char_addr+number)"
//Serial.println("char_addr[0]= "+String(*(char_addr))));
//Serial.println("char_addr[1]= "+String(*(char_addr+1)));
//Serial.println("char_addr[3]= "+String(*(char_addr+3)));
}
//########################################################
int getGliphData(const String &uniChar, int* index, int* charWidth)
{
Serial.println("uniChar data= " + uniChar);
if (uni_name)
{
for (int i = 0; uni_name[i].Length() > 0; ++i)
{
Serial.println("uni_name[" + String(i) + "]= " + uni_name[i]);
if (uniChar == uni_name[i])
{
*index = i;
/* *charWidth = char_width[i]; it works if 'i' is
* a const number at compile time.
* constant 'i' only works because the compiler
* sets the value of char_width[i] during
* compilation, char_width[i] is a constant value
* pointing to one specific location in progmem
* that the compiler knows the value of while
* compiling see:https://forum.arduino.cc/t/passing-a-pointer-to-progmem-in-function/637756/5
*/
*charWidth = pgm_read_word_near(char_width+*index);
return i;
}
}
}
return -1;
}
#include <stdio.h>
#include <iostream>
using namespace std;
//char* b[6] = new char[6];
char a[6] = {'b','c','d','e','f','g'};
char c[6] = {'a','b','d','d','f','g'};
int main()
{
char d[][6]={*a,*c};
for (int x = 0 ; x < 1; x++)
{
for(int y = 0; y<6; y++)
{
char test = d[x][y];
cout << test <<"\n";
}
}
return 0;
}
This code is C++ code. I am trying to create a class where it stores the char array. Then there is another char array of array storing already declared char variables. It compiles fine but it doesn't work out to as it should. It doesn't get me the right value that it should when the program tries to print the value
May be you meant array of pointers:
char *d[]={a,c};
typedef std::vector<char> VectorChar;
typedef std::vector< VectorChar* > VectorVectorChar;
struct V
{
V() : _v{ '0', '1', '2' } {}
VectorChar _v;
};
int main(void)
{
V arrV[5];
VectorVectorChar vvc;
for ( auto& v : arrV )
vvc.push_back( &v._v );
// print them
for ( auto pV : vvc )
{
for ( auto c : *pV )
cout << c << ' ';
cout << '\n;
}
return 0;
}
what i understood from the question that, you want to create class to store char array, which already initialized.
#include <stdio.h>
#include <iostream>
char a[6] = {'b','c','d','e','f','g'}; // Initialized character array. MAX 6
// This class will hold char array
class Test {
public:
void setName(char *name);
const char* getName();
private:
char m_name[6]; // MAX 6 ( since you already initialized(MAX 6), So no need dynamic memory allocation. )
};
void Test::setName(char *name) {
strcpy(m_name, name); // Copy, already initialized array
}
const char* Test::getName() {
return m_name;
}
int main(int argc, char** argv) {
{
Test foobar;
foobar.setName( a ); // set the pointer which point to starting of the initialized array.
cout << foobar.getName();
return 0;
}
char a[6] = {'b','c','d','e','f','\0'};
char c[6] = {'a','b','d','d','f','\0'};
char* d[]= {a,c};
for (int x = 0 ; x < 2; x++)
{
for(int y = 0; y < 6; y++)
{
char test = d[x][y];
cout << test << "\n";
}
}
return 0;
How do I convert a uchar to an int?
If uchar is typedef of unsigned char, then it's simple:
unsigned char c = 'A';
int i = c; //automatic conversion uchar into int!
Casting (i.e int i = (int)c) is overkill.
#include <iostream>
int main(int argc, char *argv[])
{
unsigned char x = '\xff';
std::cout << static_cast<int>(x) << std::endl;
return 0;
}
Output:
255
Try:
void func(int x)
{
}
int main()
{
unsigned char c = 'c';
int t = c; // auto converts to int
func(c); // Works the same (auto converts)
func(t); // Or use the t variable.
}
Simply assigning it to a int variable for implicit conversion
unsigned char c = 'c';
int i = c;
Or explicitly type-casting the variable
unsigned char c = 'c';
int i = (int)c;