Good day,
I am receiving userIds as integer on an ESP32, I would like to convert them to the corresponding name. Currently I solve this with the switch statement as shown below. Is there a more elegant solution?
String ReturnName(int userId) {
Switch(userId) {
case 20:
{
return "John";
}
break;
case 44:
{
return "Maria";
}
break;
case 4:
{
return "Herrensen";
}
break;
}
}
First of all having a break after a return doesn't make sense. Remove them.
Switch(userId) {
case 4: return "Herrensen";
case 20: return "John";
case 44: return "Maria";
}
Isn't to bad imho.
Alternatively you could use an array.
const char* lut[45];
lut[20] = "John";
lut[44] = "Maria";
lut[4] = "Herrensen";
But this is not very memory efficient if you don't have consecutive user ids. At the moment you have reserved memory for 45 pointers but only used 3 so most of the memory is wasted. and you have to make sure that you only index valid elements within the array.
You could use a hash table but that is usually not a good idea on Arduino due to limited memory.
For an ESP32 target, you might consider a std::map
#include <map>
std::map<byte, const char*> list;
void setup() {
list[4] = "Anna";
list[20]= "John";
Serial.begin(115200);
for (auto const & n : list) {
Serial.print(n.first); Serial.print(":"); Serial.println(n.second);
}
}
An ESP is not as restricted in RAM and c++ stdlib as a real Arduino
Related
I'm trying to write values to some OPC UA nodes with Qt and open62541. For that I have to know the different data types of the nodes. Every time I try to read the data type of a node, I get a Boolean type instead of an int32. It is the correct node in the list and I can read all nodes. Can someone please help me?
//This is how I add nodes to the server. This is an example with the node DBW0.
//After adding the nodes to the server, each node will be append to the _nodeList.
void OPCConnection::AddNodeToServer()
{
QOpcUaNodeCreationAttributes attributes;
attributes.setDataTypeId(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int16));
attributes.setValueRank(-2); // Scalar or array
attributes.setAccessLevel(QOpcUa::AccessLevelBit::CurrentWrite);
attributes.setUserAccessLevel(QOpcUa::AccessLevelBit::CurrentWrite);
QOpcUaAddNodeItem item;
item.setParentNodeId(QOpcUa::QExpandedNodeId("ns=2;s=PLC1.S7_300.DB120"));
item.setReferenceTypeId(QOpcUa::nodeIdFromReferenceType(QOpcUa::ReferenceTypeId::Organizes));
item.setRequestedNewNodeId(QOpcUa::QExpandedNodeId("ns=2;s=DBW0"));
item.setNodeClass(QOpcUa::NodeClass::Variable);
item.setNodeAttributes(attributes);
_client->addNode(item);
}
//This is how I read the nodes.
void OPCConnection::readNode()
{
if (_client->state() == QOpcUaClient::ClientState::Connected)
{
for (int i = 0; i < _nodeList->count(); i++)
{
_nodeList->at(i)->readAttributes(QOpcUa::NodeAttribute::DataType);
_nodeList->at(i)->readAttributes(QOpcUa::NodeAttribute::Value);
}
}
}
//After reading I want to write.
void OPCConnection::setNodeValue(const QVariant value, const int index)
{
_nodeList->at(index)->writeValueAttribute(value,
_nodeList->at(index)->attribute(QOpcUa::NodeAttribute::DataType).
value<QOpcUa::Types>());
}
I can only write boolean nodes as a datatype, because each node has a boolean value as a datatype.
I found a solution for my case, but I'm not happy with that.
Because I can't distinguish a 16 bit Integer and a 32 bit Integer.
void OPCConnection::setNodeValue(const QVariant value, const int index)
{
_nodeList->at(index)->writeValueAttribute(value,
selectType(_nodeList->at(index)->attribute(QOpcUa::NodeAttribute::Value).type()));
}
QOpcUa::Types OPCConnection::selectType(const QVariant::Type type)
{
switch (type)
{
case QVariant::Bool:
return QOpcUa::Types::Boolean;
case QVariant::UInt:
return QOpcUa::Types::UInt32;
default:
return QOpcUa::Types::UInt16;
}
}
I find many answers to my question and they all work. My question is are they all equal in speed and memory. How can I tell what is faster and uses less memory. I don't normally use the Marshal and GCHandle classes. So I am totally green.
public static object RawDeserializer(byte[] rawData, int position, Type anyType)
{
int rawsize = Marshal.SizeOf(anyType);
if (rawsize > rawData.Length)
return null;
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(rawData, position, buffer, rawsize);
object retobj = Marshal.PtrToStructure(buffer, anyType);
Marshal.FreeHGlobal(buffer);
return retobj;
}
public static T RawDeserializer<T>(byte[] rawData, int position = 0)
{
int rawsize = Marshal.SizeOf(typeof(T));
if (rawsize > rawData.Length)
{
throw new DataMisalignedException("byte array is not the correct size for the requested type");
}
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(rawData, position, buffer, rawsize);
T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T));
Marshal.FreeHGlobal(buffer);
return retobj;
}
public static T RawDeserializer<T>(byte[] bytes) where T : struct
{
T stuff;
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
stuff = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
return stuff;
}
I am getting the desired results form all 3 implementations.
First and second are almost identical: difference is that you do not unbox (cast to T:struct) the result in the first example, I'd assume that you'll unbox it later though.
Third option does not copy memory to the unmanaged heap, it just pins it in manageed heap, so I'd assume it will allocate less memory and will be faster. I don't pretend to be a golden source of truth though, so just go and make performance testing of these options :) BenchmarkDotNet is a great framework for performance testing and may help you a lot.
Also third option could be more concise:
public static unsafe T RawDeserializer<T>(byte[] bytes) where T : struct
{
fixed (byte* p = bytes)
return Marshal.PtrToStructure<T>((IntPtr)p);
}
You need to change project settings to allow unsafe code though:
To do not be totally green, I'd strongly recommend to read a book CLR via C#, Chapter 21 'The Managed Heap and Garbage Collection'.
void ChangeStates(void)
{
int i;
for (i=0; i<CELLS; i++)
{
switch (state[i])
{
case IMMUNE:
timer[i]--;
if(timer[i]==0)
state[i] = HEALTHY;
break;
case INFECTED:
timer[i]--;
if(timer[i]==0)
{
state[i] = IMMUNE;
timer[i] = IMM_TIME;
}
break;
case EXPOSED:
timer[i]--;
if(timer[i]==0)
{
state[i] = INFECTED;
timer[i] = INF_TIME;
}
break;
default:
} //Here is the error
}
}
The program intend to do something about disease dynamics in plants, and use the switch function to change the state of cells.
I am using Qt Creator 5.5 as a beginner.
But I do not know how to fix this error.
Thanks in advance
'default' has to be followed by a statement, 'break;' will work. Or remove the default as was suggested, this might mean better warnings also (unhandled enumeration value in switch, which is always good to catch).
You could also do 'qFatal' in the default to again catch an unhandled state[] value in the switch.
Consider these C functions:
#define INDICATE_SPECIAL_CASE -1
void prepare (long *length_or_indicator);
void execute ();
The prepare function is used to store a pointer to a delayed long * output variable.
It can be used in C like this:
int main (void) {
long length_or_indicator;
prepare (&length_or_indicator);
execute ();
if (length_or_indicator == INDICATE_SPECIAL_CASE) {
// do something to handle special case
}
else {
long length = lengh_or_indicator;
// do something to handle the normal case which has a length
}
}
I am trying to achieve something like this in Vala:
int main (void) {
long length;
long indicator;
prepare (out length, out indicator);
execute ();
if (indicator == INDICATE_SPECIAL_CASE) {
// do something to handle special case
}
else {
// do something to handle the normal case which has a length
}
}
How to write the binding for prepare () and INDICATE_SPECIAL_CASE in Vala?
Is it possible to split the variable into two?
Is it possible to avoid using pointers even though the out variable is written to after the call to prepare () (in execute ())?
The problem with using out is that Vala is going to generate lots of temporary variables along the way, which will make the reference wrong. What you probably want to do is create a method in your VAPI that hides all this:
[CCode(cname = "prepare")]
private void _prepare (long *length_or_indicator);
[CCode(cname = "execute")]
private void _execute ();
[CCode(cname = "prepare_and_exec")]
public bool execute(out long length) {
long length_or_indicator = 0;
prepare (&length_or_indicator);
execute ();
if (length_or_indicator == INDICATE_SPECIAL_CASE) {
length = 0;
return false;
} else {
length = lengh_or_indicator;
return true;
}
}
I wrote a program to test my binary tree and when I run it, the program seems to crash (btree.exe has stopped working, Windows is checking for a solution ...).
When I ran it through my debugger and placed the breakpoint on the function I suspect is causing it, destroy_tree(), it seemed to run as expected and returned back to the main function. Main, in turn, returned from the program but then the cursor jumped back to destroy_tree() and looped recusively within itself.
The minimal code sample is below so it can be ran instantly. My compiler is MinGW and my debugger is gdb (I'm using Code::Blocks).
#include <iostream>
using namespace std;
struct node
{
int key_value;
node *left;
node *right;
};
class Btree
{
public:
Btree();
~Btree();
void insert(int key);
void destroy_tree();
private:
node *root;
void destroy_tree(node *leaf);
void insert(int key, node *leaf);
};
Btree::Btree()
{
root = NULL;
}
Btree::~Btree()
{
destroy_tree();
}
void Btree::destroy_tree()
{
destroy_tree(root);
cout<<"tree destroyed\n"<<endl;
}
void Btree::destroy_tree(node *leaf)
{
if(leaf!=NULL)
{
destroy_tree(leaf->left);
destroy_tree(leaf->right);
delete leaf;
}
}
void Btree::insert(int key, node *leaf)
{
if(key < leaf->key_value)
{
if(leaf->left!=NULL)
insert(key, leaf->left);
else
{
leaf->left = new node;
leaf->left->key_value = key;
leaf->left->left = NULL;
leaf->left->right = NULL;
}
}
else if (key >= leaf->key_value)
{
if(leaf->right!=NULL)
insert(key, leaf->right);
else
{
leaf->right = new node;
leaf->right->key_value = key;
leaf->right->left = NULL;
leaf->right->right = NULL;
}
}
}
void Btree::insert(int key)
{
if(root!=NULL)
{
insert(key, root);
}
else
{
root = new node;
root->key_value = key;
root->left = NULL;
root->right = NULL;
}
}
int main()
{
Btree tree;
int i;
tree.insert(1);
tree.destroy_tree();
return 0;
}
As an aside, I'm planning to switch from Code::Blocks built-in debugger to DDD for debugging these problems. I heard DDD can display visually pointers to objects instead of just displaying the pointer's address. Do you think making the switch will help with solving these types of problems (data structure and algorithm problems)?
Your destroy_tree() is called twice, you call it once and then it gets called after the execution leaves main() from the destructor.
You may think it should work anyway, because you check whether leaf!=NULL, but delete does not set the pointer to NULL. So your root is not NULL when destroy_tree() is called for the second time,
Not directly related (or maybe it is) to your problem, but it's good practice to give structs a constructor. For example:
struct node
{
int key_value;
node *left;
node *right;
node( int val ) : key_val( val ), left(NULL), right(NULL) {}
};
If you do this, your code becomes simpler, because you don't need worry about setting the pointers when you create a node, and it is not possible to forget to initialise them.
Regarding DDD, it;'s a fine debugger, but frankly the secret of debugging is to write correct code in the first place, so you don't have to do it. C++ gives you a lot of help in this direction (like the use of constructors), but you have to understand and use the facilities it provides.
Btree::destroy_tree doesn't set 'root' to 0 after successfully nuking the tree. As a result, the destructor class destroy_tree() again and you're trying to destroy already destroyed objects.
That'll be undefined behaviour then :).
Once you destroy the root.
Make sure it is NULL so it does not try to do it again (from the destructor)
void Btree::destroy_tree(node *leaf)
{
if(leaf!=NULL)
{
destroy_tree(leaf->left);
destroy_tree(leaf->right);
delete leaf;
leaf = NULL; // add this line
}
}