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;
}
Related
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
I've a C++ library (really a wrapper for another C++ library) and I need pass some structs to my C application.
I don't know how allocate the memory dynamically.
I get a segmentation fault.
library.h
struct my_substruct {
unsigned char id ;
time_t date ;
char *info ;
};
typedef struct my_substruct My_substruct ;
struct my_struct {
char *description ;
unsigned char value ;
My_substruct *substruct ;
};
typedef my_struct My_struct ;
library.cpp
unsigned char getStructs(My_struct *structs)
{
vector <structCPPLibrary> structsCPPLibrary = getFromCPPLibrary();
unsigned char numStructs = structsCPPLibrary.size();
structs = (My_struct *)malloc(numStructs*sizeof(My_struct));
unsigned char indexStruct = 0;
for (auto s : structsCPPLibrary)
{
structs[indexStruct].description = (char *)malloc(s.description.size() + 1);
strcpy(structs[indexStruct].description, s.description.c_str()); // In 's' is a std::string
structs[indexStruct].value = s.value; // In 's' is an unsigned char too
unsigned char numSubstructs = s.substructs.size(); // In 's' is a vector of Substructs
structs[indexStruct].substruct = (My_substruct *)malloc(numSubstructs*sizeof(My_substruct));
unsigned char indexSubstruct = 0;
for (auto sub : s.substruct) {
structs[indexStruct].substruct[indexSubstruct].id = sub.id; // In 'sub' is an unsigned char too
structs[indexStruct].substruct[indexSubstruct].date = sub.date; // In 'sub' is a time_t too
structs[indexStruct].substruct[indexSubstruct].info = (char *)malloc(sub.info.size() + 1);
strcpy(structs[indexStruct].substruct[indexSubstruct].info, sub.info.c_str()); // In 'sub' is a std::string
indexSubstruct++;
}
indexStruct++;
}
return indexStruct;
}
main.c
void getStructFromWrapper(void)
{
My_struct *structs;
unsigned char numStruct = getStructs(structs);
show_content(structs);
}
Change
unsigned char getStructs(My_struct *structs) {
...
}
getStructs(structs);
To
unsigned char getStructs(My_struct **p_structs) {
// C function can't be pass by reference, so convert to a reference here
auto& struct = *p_structs;
...
}
...
getStructs(&structs);
Your problem is that your struct = ... line is not modifying the value of structs in getStructFromWrapper.
I have the following struct:
const int max_stu= 100;
const int max_name =100;
const int max_add=100;
struct stu{
int id;
char name[max_name];
char addr[max_add];
};
stu student[max_stu];
One of my friends already give me this code
struct stu{
int id;
char name[max_name];
char addr[max_add];
void remove(stu temp){
temp.id= 0;
temp.name= "";
temp.addr= "";
}
for (int i=0;i < max_stu; i++){
stu.remove(student[i]);
}
but it have problem in this part
temp.name= "";
because of the type of char and
max_name=100
and on this part
stu.remove(student[i]);
because its not a class. How can I fix this?
Could someone explain me the following mistakes and tell me how to fix them (written as comments)?
The structure I have:
const int max = 1000;
const int MAX = 30;
struct student_t
{
int k;
char* name[MAX];
char** bez[MAX];
};
With this function I must write a member of this structure(members<=1000) and bez. (<=30) for every member:
int write (student_t *field[max], int i)
{
student_t *pointerfield= new student_t;
printf("Number:....");
std::cin>>pointerfield->number;
if (pointerfield->number>1 && pointerfield->k<999999)
{
if (binarsearch(&field[max],i,(pointerfield->number))!=-1)
{
return i;
}
else
{
printf("\nName:.....");
readstr();
pointerfield->name=readstr();
// insupportable types of allocation of int to char* [30]
std::cout<<std::endl;
int counter=0;
std::cout<<"Do you want to add bez.? j,n"<<std::endl;
char a;
std::cin>>a;
if(a=='j' && counter<MAX)
{
std::cout<<"Bez.:";
readstr();
pointerfield->bez[MAX]=readstr();
// char * can not be converted into assignment to char **
counter++;
}
// ...
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;