I'm trying to pass a string as part of a structure over the network using the rpcgen packages. This is my IDL code :
struct param
{
char* name;
int voterid;
};
program VOTECLIENT_PROG
{
version VOTECLIENT_VERS
{
string ZEROIZE() = 1;
string ADDVOTER(int) = 2;
string VOTEFOR(param) = 3;
string LISTCANDIDATES() = 4;
int VOTECOUNT(string) = 5;
} = 1;
} = 0x2345111;
Somehow, the string is being truncated to a single character at the server. For example, if I pass name = "abc", I get "a" at the server. It looks like this is happening because of some issue inside the stubs, but I can't seem to figure out where the bug is.
My client code for the function that passes the string as an argument :
void
voteclient_prog_1(char *host, char* c, int id)
{
CLIENT *clnt;
char * *result_3;
param votefor_1_arg;
#ifndef DEBUG
clnt = clnt_create (host, VOTECLIENT_PROG, VOTECLIENT_VERS, "udp");
if (clnt == NULL) {
clnt_pcreateerror (host);
exit (1);
}
#endif /* DEBUG */
votefor_1_arg.name = c;
votefor_1_arg.voterid = id;
result_3 = votefor_1(&votefor_1_arg, clnt);
if (result_3 == (char **) NULL) {
clnt_perror (clnt, "call failed");
}
clnt_perror (clnt, "call failed");
#ifndef DEBUG
clnt_destroy (clnt);
#endif /* DEBUG */
}
int
main (int argc, char *argv[])
{
char *host;
int id;
char* c = new char[20];
if (argc < 4) {
printf ("usage: %s server_host name voterid\n", argv[0]);
exit (1);
}
host = argv[1];
c = argv[2];
id = atoi(argv[3]);
voteclient_prog_1 (host, c, id);
exit (0);
}
Any help will be greatly appreciated.
From rpcgen Programming Guide, 6.9. Special Cases:
Strings: C has no built-in string type, but instead uses the
null-terminated “char *” convention. In XDR language, strings are
declared using the “string” keyword, and compiled into “char *”s in
the output header file. The maximum size contained in the angle
brackets specifies the maximum number of characters allowed in the
strings (not counting the NULL character). The maximum size may be
left off, indicating a string of arbitrary length.
Examples:
string name<32>; --> char *name;
string longname<>; --> char *longname;
So, you should declare name like above, e. g. string name<20>;.
Adding something to the comment above, the usage of this kind of array in rpcgen is like follows:
In your struct declare arrays (of any type) like this
struct myStruct {
//In my case I used an array of floats
float nums<>;
}
This declare an "array" of type float. This kind of struct has two members of variables
struct {
u_int nums_len;
float *nums_val;
}nums;
Now you can allocate memory to the array of type float:
//totNums is the number of elements in the array
nums.nums_val = (float*)malloc(totNums*sizeof(float));
nums.nums_len = totNums;
Now in the server you can use your array with all the elements.
Related
struct Label {
char *string;
int mode;
};
I have char pointer in my struct. *string must be initialized on create as char[10] without using malloc-like functions (arduino project with limited memory). I can simply change struct body to
struct Label {
char string[10];
int mode;
};
but also I need to set label.string = "asd" many times with different string size. How to solve this?
You can potentially fill the label.string with a null terminated string value using the snprintf function.
Examples:
snprintf(label.string, 10, "asd" );
snprintf(label.string, 10, "%c%c%c", 'a','s','d');
snprintf(label.string, sizeof(label.string), "%s%d", "asd", 1);
It essentially works like normal printf function but the first two arguments specify the destination buffer and the size of this buffer. When you try to copy a string exceeding the boundaries of that buffer, the copied string will be truncated to fit the buffer. Remember the second argument should not be greater then the actual buffer size because you can end up overwriting some other memory region.
See: http://www.cplusplus.com/reference/cstdio/snprintf/
Ok, the only way I know to do this is to store char pointer, which is set to static-allocated array pointer by default and can be changed on runtime:
struct Label {
char stringArray[4];
char *string = stringArray;
int mode;
};
int main() {
Label label;
label.stringArray[0] = 'a';
label.stringArray[1] = 'b';
label.stringArray[2] = 'c';
label.stringArray[3] = '\0';
cout << label.string << endl;
label.string = "bbbbbb";
cout << label.string << endl;
return 0;
}
How would you structure a set of parameter value pairs if the parameter data types are different?
Suppose you have a large set of parameters that are bound in a larger software like this:
string name;
int counter;
//...
float temperature;
bool enableState;
To set the value of a parameter manually a string should be accepted via commandline (e.g. "Name=Eric","Counter=100","Temperature=20.0",...).
The search is for a suitable structure for the parameters so that they (or at least the information to read/write them) can be stored in an array. Since the values have different data types, they cannot be stored in an array, but is it purposeful with their pointers (?).
How can the (string) values of the input string be assigned to variables that have fix data types?
Whatever I'm trying doesn't lead to anything or it is a tremendous effort, which makes me believe that a possible (and simple) solution might not be trivial.
The code is supposed to run on an arduino.
Edit:
I found a way to do it and it seems to work quite well. But I'm not sure, maybe I did fundamental misstakes. Could you confirm that the code in my answer is legal, so I can set the question to 'solved'?
You could use a std::map<std::string, std::function<void(std::string&)>> variableMap and fill it up with lambdas like this.
std::string name;
int counter;
...
variableMap["Name"] = [&](std::string& val) {
name = val;
}
variableMap["Counter"] = [&](std::string& val) {
counter = std::stoi(val);
}
And then use it something like this
//Get the input and parse it, eg "Name=Chris"
std::string variableName; // Holds "Name"
std::string variableValue; // Holds "Chris"
if (variableMap.find(variableName) != variableMap.end())
variableMap[variableName](variableValue);
Take a look at std::variant or std::any (both available in C++17). If you're using an earlier standard, boost provides boost::variant and boost::any.
You may be able to have a struct store the data as a unsigned char * and then have some meta data about how to decode the unsigned char * in the struct. Something like
typedef struct FlexibleStruct {
char * metaData;
// this data type here doesn't matter because we will cast it to what we want
unsigned char * data;
} FlexibleStruct;
Then when you want to store it:
FlexibleStruct f;
f.metaData = "float";
f.data = (unsigned char *)floatToStore; // This should be a pointer to a float on the heap
and to retrieve the data:
if(strcmp(f.metaData, "float") == 0) {
float *retrievedFloat = (float *)f.data;
printf("%f\n", *retrievedFloat);
}
This will work with any data type (even structs) in any version of c or c++!
I tested this on gcc 6.4.0, but I don't know if it will work for avr-g++. I can provide more information if you'd like.
I found a way to do it, but I'm not sure if I will get problems with memory allocation. Beside others I also need string parameters linked to global string variables. The parameters need to be global as well. I'm linking the variables to the parameters with thier memory address, but I'm not sure if that's legal...
My solution is:
#include <iostream>
#include <string>
enum Access {NO_ACCESS,READ,WRITE,READ_WRITE};
enum DataType {INTEGER,FLOAT,STRING,BOOLEAN};
// parameter-class
class Parameter {
public:
long int address;
string (*setEventAddress) ();
string (*getEventAddress) ();
string parameterName;
Access access;
DataType datatype;
string set(string value) {
if(!(access==WRITE || access==READ_WRITE)){
return "ERROR: no write access";
}
string response="";
switch (datatype){
case INTEGER:
response = setInteger(value,address);
break;
case FLOAT:
response = setFloat(value,address);
break;
case STRING:
response = setString(value,address);
break;
case BOOLEAN:
response = setBool(value,address);
break;
default:
response = "ERROR: unknown data type";
break;
}
if(setEventAddress!=NULL){
string eventResponse=setEventAddress();
}
return response;
}
string get(){
if(!(access==READ || access==READ_WRITE)){
return "ERROR: no read access";
}
string response="";
switch (datatype){
case INTEGER:
response = getInteger(address);
break;
case FLOAT:
response = getFloat(address);
break;
case STRING:
response = getString(address);
break;
case BOOLEAN:
response = getBool(address);
break;
default:
response = "ERROR: unknown data type";
break;
}
if(setEventAddress!=NULL){
string eventResponse=setEventAddress();
}
return response;
}
string setInteger(string valueRaw, long int address) {
int value = stoi(valueRaw);
*(int*)(address)=value;
return "Done";
}
string setFloat(string valueRaw, long int address) {
float value = stof(valueRaw);
*(float*)(address)=value;
return "Done";
}
string setString(string valueRaw, long int address) {
string value = valueRaw;
*(string*)(address)=value;
return "Done";
}
string setBool(string valueRaw, long int address) {
bool value = true;
if (valueRaw.compare("true") == 0) {
*(bool*)(address)=true;
} else {
*(bool*)(address)=false;
}
return "Done";
}
string getInteger(long int address) {
int& i = *reinterpret_cast<int*>(address);
return to_string(i);
}
string getFloat(long int address) {
float& f = *reinterpret_cast<float*>(address);
return to_string(f);
}
string getString(long int address) {
string& s = *reinterpret_cast<string*>(address);
return s;
}
string getBool(long int address) {
bool& b = *reinterpret_cast<bool*>(address);
return to_string(b);
}
Parameter(string n, Access a, DataType d, long int varPointer, string (*setFuncPointer)(), string (*getFuncPointer)());
};
// constructor for parameter
Parameter::Parameter(string n, Access a, DataType d, long int varPointer, string (*setFuncPointer)()=NULL, string (*getFuncPointer)()=NULL) {
parameterName=n;
access=a;
datatype=d;
address=varPointer;
setEventAddress=setFuncPointer;
getEventAddress=getFuncPointer;
}
// functions optionally executed when parameter becomes set/get
string exampleSetEvent() {
// do anything when parameter is set
return m;
}
string exampleGetEvent() {
// do anything when parameter is read
return m;
}
// a parameter constists of the variable and an object of the parameter-class
// and is simply introduced by adding only two lines
string name;
Parameter Name ("Name", READ_WRITE, STRING, (long int) &name);
int counter;
Parameter Counter ("Counter",READ_WRITE, STRING, (long int) &counter, exampleSetEvent, example);
float temperature;
Parameter Temperature ("Temperature", READ_WRITE, STRING, (long int) &temperature);
bool enableState;
Parameter EnableState ("EnableState", READ_WRITE, STRING, (long int) &enableState);
Does anyone see any problems?
In my understanding the memory address of a global object necessarily must stay the same during the entire runtime. Is that correct?
How about strings? As the size of strings is not given explicitly, how can the memory for a string be allocated at compile time? Only the start address of a string gets allocated, I guess.
I have an Arduino that controls timers. The settings for timers are stored in byte arrays. I need to convert the arrays to strings to SET a string on an external Redis server.
So, I have many arrays of bytes of different lengths that I need to convert to strings to pass as arguments to a function expecting char[]. I need the values to be separated by commas and terminated with '\0'.
byte timer[4] {1,5,23,120};
byte timer2[6] {0,0,0,0,0,0}
I have succeeded to do it manually for each array using sprintf() like this
char buf[30];
for (int i=0;i<5;i++){ buf[i] = (int) timer[i]; }
sprintf(buf, "%d,%d,%d,%d,%d",timer[0],timer[1],timer[2],timer[3],timer[4]);
That gives me an output string buf: 1,5,23,120
But I have to use a fixed number of 'placeholders' in sprintf().
I would like to come up with a function to which I could pass the name of the array (e.g. timer[]) and that would build a string, probably using a for loop of 'variable lengths' (depending of the particular array to to 'process') and many strcat() functions. I have tried a few ways to do this, none of them making sense to the compiler, nor to me!
Which way should I go looking?
Here is the low tech way you could do it in normal C.
char* toString(byte* bytes, int nbytes)
{
// Has to be static so it doesn't go out of scope at the end of the call.
// You could dynamically allocate memory based on nbytes.
// Size of 128 is arbitrary - pick something you know is big enough.
static char buffer[128];
char* bp = buffer;
*bp = 0; // means return will be valid even if nbytes is 0.
for(int i = 0; i < nbytes; i++)
{
if (i > 0) {
*bp = ','; bp++;
}
// sprintf can have errors, so probably want to check for a +ve
// result.
bp += sprintf(bp, "%d", bytes[i])
}
return buffer;
}
an implementation, assuming that timer is an array (else, size would have to be passed as a parameter) with the special handling of the comma.
Basically, print the integer in a temp buffer, then concatenate to the final buffer. Pepper with commas where needed.
The size of the output buffer isn't tested, mind.
#include <stdio.h>
#include <strings.h>
typedef unsigned char byte;
int main()
{
byte timer[4] = {1,5,23,120};
int i;
char buf[30] = "";
int first_item = 1;
for (i=0;i<sizeof(timer)/sizeof(timer[0]);i++)
{
char t[10];
if (!first_item)
{
strcat(buf,",");
}
first_item = 0;
sprintf(t,"%d",timer[i]);
strcat(buf,t);
}
printf(buf);
}
I have this code, but is impossible to compile with g++ or msvc. I am trying to make a custom type CharNw that I can use it as string, in existing all string routines or pass as argument all existing functions:
#include <string.h>
#include <stdio.h>
void fx(unsigned int x)
{
/*this is the reason of all this
but is ok, not is problem here */
.....
}
class CharNw
{
int wt;
char cr;
public:
CharNw() { wt = -1; cr = '\0'; }
CharNw( char c) { if wt > 0 fx( (unsigned int) wt); cr = c; }
operator char () { if wt > 0 fx( (unsigned int) wt); return cr ;}
assgn( int f) { wt = f;}
};
int main(void)
{
CharNw hs[40]; //it is ok
CharNw tf[] = "This is not working, Why?\n";
char dst[40];
strcpy(dst, tf); //impossible to compile
printf("dst = %s, tf = %s", dst, tf); //too
return 0;
}
Can help me?
Line by line.
CharNw hs[40]; //it is ok
The above is an array of CharNw objects with a capacity of 40 elements. This is good.
CharNw tf[] = "This is not working, Why?\n";
On the right hand side (RHS) of the assignment, you have a type char const * const* and on the left you have an array ofCharNw. TheCharNw` is not a character, so you have a problem here. Prefer to have both sides of an assignment to have the same type.
char dst[40];
An array of characters. Nothing more, nothing less. It has a capacity of 40 characters. The dst array is not a string. You should prefer to use #define for your array capacities.
strcpy(dst, tf); //impossible to compile
The strcpy requires both parameters to be pointers to char. The left parameter can be decomposed to a pointer to the first char of the array. The tf is an array of CharNw which is not compatible with an array of char nor a pointer to char.
printf("dst = %s, tf = %s", dst, tf); //too
The printf format specifier %s requires a pointer to a character, preferably a C-Style, nul terminated array (or sequence) of characters. The tf parameter is an array of CharNw which is not an array of characters nor a pointer to a single character or a C-Style string.
Edit 1: Conversion Operators
The method operator char () in your class converts a character variable to a CharNw variable. It does not apply to pointers nor arrays.
You would need some messy pointer conversion functions.
Here is an examples:
const unsigned int ARRAY_CAPACITY = 40U;
const char text[] = "I am Sam. Sam I am."
CharNw tf[ARRAY_CAPACITY];
for (unsigned int i = 0U; i < sizeof(text); ++i)
{
tf[i] = text[i]; // OK, Converts via constructor.
}
for (unsigned int i = 0U; i < sizeof(text); ++i)
{
printf("%c", tf[i]); // The tf[i] should use the operator char () method.
}
A better approach is to declare a class using std::basic_string, rather than trying to squeeze your class into the C-Style string functions.
For example:
class StringNw : public std::basic_string<CharNw>
{
};
I my trying to copy a value into a char.
my char array is
char sms_phone_number[15];
By the way, could tell me if I should write (what the benefic/difference?)
char * sms_phone_number[15]
Below displays a string: "+417611142356"
splitedString[1]
And I want to give that value to sms_from_number
// strcpy(sms_from_number,splitedString[1]); // OP's statement
strcpy(sms_phone_number,splitedString[1]); // edit
I've got an error, I think because splitedString[1] is a String, isn't?
sim908_cooking:835: error: invalid conversion from 'char' to 'char*'
So how can I copy it correctely.
I also tried with sprintf without success.
many thank for your help.
Cheers
I declare spliedString like this
// SlitString
#define NBVALS 9
char *splitedString[NBVALS];
I have that function
splitString("toto,+345,titi",slitedString)
void splitString(char *ligne, char **splitedString)
{
char *p = ligne;
int i = 0;
splitedString[i++] = p;
while (*p) {
if (*p==',') {
*p++ = '\0';
if (i<NBVALS){
splitedString[i++] = p;
}
}
else
{
p++;
}
}
while(i<NBVALS){
splitedString[i++] = p;
}
}
If I do a for with splitedString display, it display this
for(int i=0;i<4;i++){
Serialprint(i);Serial.print(":");Serial.println(splitedString[i]);
}
//0:toto
//1:+4176112233
//2:14/09/19
I also declared and want to copy..
char sms_who[15];
char sms_phone_number[15];
char sms_data[15];
//and I want to copy
strcpy(sms_who,splitedString[0]
strcpy(sms_phone_number,splitedString[1]
strcpy(sms_date,splitedString[2]
I know, I am very confused with char and pointer * :o(
The declaration:
char * SplittedString[15];
Declares an array of pointers to characters, a.k.a. C-style strings.
Given:
const char phone1[] = "(555) 853-1212";
const char phone2[] = "(818) 161-0000";
const char phone3[] = "+01242648883";
You can assign them to your SplittedString array:
SplittedString[0] = phone1;
SplittedString[1] = phone2;
SplittedString[2] = phone3;
To help you a little more, the above assignments should be:
SplittedString[0] = &phone1[0];
SplittedString[1] = &phone2[0];
SplittedString[2] = &phone3[0];
By definition, the SplittedStrings array contains pointers to single characters, so the last set of assignments is the correct version.
If you are allowed, prefer std::string to char *, and std::vector to arrays.
What you need is a vector of strings:
std::vector<std::string> SplittedStrings(15);
Edit 1:
REMINDER: Allocate space for your spliedString.
Your spliedString should either be a pre-allocated array:
char spliedString[256];
or a dynamically allocated string:
char *spliedString = new char [256];
Strings and Chars can be confusing for noobs, especially if you've used other languages that can be more flexible.
char msg[40]; // creates an array 40 long that can contains characters
msg = 'a'; // this gives an error as 'a' is not 40 characters long
(void) strcpy(msg, "a"); // but is fine : "a"
(void) strcat(msg, "b"); // and this : "ab"
(void) sprintf(msg,"%s%c",msg, 'c'); // and this : "abc"
HTH