I am currently writing code that will read an xml file using the pugixml module. In my xml file I have several shape nodes that are differentiated by a shape_id attribute that gets a number.
For a specific need I have the shape_id number stored in an integer although in xml file the value of the respective attribute is of course a string (or char, have to check).
In order to retrieve the specific shape node, e.g. that with the attribute shape_id = 2 (just to give an example) I have written the function below.
pugixml provides a function find_child_by_attribute() with this call definition:
PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
As you can see it requests the attribute value as a char pointer.
I manage to make it work with the two lines as in the code:
char char_shape_id = static_cast<char>(shape_id);
char* shape_id_ptr = &char_shape_id;
First I cast the integer shape_id into a char and then I take the address of this char and store it into a char pointer. I think it is an awkward way to do but I cannot figure out how to do this in a more elegant way.
I've tried following but failed.
char* shape_id_ptr = &(static_cast<char>(shape_id));
This seems to be invalid code.
What seems also to work is when I have following function call in the return line:
return root_node.child(sNodeNames.case_def_name.c_str()).find_child_by_attribute(sNodeNames.shape_name.c_str(),"item_id", (char*)shape_id);
I also tried to have a unique_ptr or shared_ptr instead of the raw char pointer but somehow I cannot figure out how to make this pointer cooperate with the find_child_by_attribute(). My shared pointer definition is currently
char char_shape_id = static_cast<char>(shape_id);
std::shared_ptr<char> shape_id_ptr(&char_shape_id);
but as I said, this is not accepted by the function.
//returns the node shape with a given shape_id number
pugi::xml_node nXml::tXmlShape::GetShapeNode(const int shape_id, const pugi::xml_node& root_node)
{
char char_shape_id = static_cast<char>(shape_id);
char* shape_id_ptr = &char_shape_id;
nXml::sXmlNodeNames sNodeNames;
nXml::sXmlShapeTypes sShapeTypes;
return root_node.child(sNodeNames.case_def_name.c_str()).find_child_by_attribute(sNodeNames.shape_name.c_str(),"item_id", shape_id_ptr);
}
;
Apart from the pretty ugly return line phrasing is there a more elegant way to accomplish the transmission of a char pointer of an originally integer value to the function and how could I realize this with a unique_ptr respectively shared_ptr?
Related
I am teaching myself C++ by doing microcontroller projects. My current project is using a pair or Adafruit Feather packet radios. The library functions for the radio packets require a C-style string (I believe) which I understand to be an array of char's.
I have already set up an enum to reflect the various actions of the receiver and would like to send that status back to the transmitter. So I want to turn an enum into an array of char's.
In googling ways to convert enum to array of chars, the easiest (for me to understand) was passing the enum variable to a function with a switch statement that would return a char string. But when I try to put the return into my char array, I get the error "invalid conversion from 'char*' to 'char' [-fpermisive]". I've also been struggling to get my head around arrays and pointers to arrays which is still pretty fuzzy in my head.
Here are some snippets of my code which I hope will show enough of what I'm trying to do.
...from my main function
BLINKING_RIGHT, //0
BLINKING_LEFT, //1
FLASHING, //2
SHOWING_BATTERY,//3
NONE //4
};
...and the two functions that process the enum to send
void SendStatus()
{
char data[20] {EnumToString(currentAction)}; //This is the line showing the error
//itoa (data,static_cast<int>(currentAction),10);
//data[0]=static_cast<uint8_t>(currentAction);
//data[1]=0;
rf69.send(data, sizeof(data));
rf69.waitPacketSent();
Serial.println("Sent a reply");
}//end_function SendStatus()
char* EnumToString(CurrentAction inputEnum)
{
char *statusString;
switch(inputEnum)
{
case 0:
statusString = "BLINKING_RIGHT"; //0
return statusString;
break;
case 1:
statusString = "BLINKING_LEFT"; //1
return statusString;
break;
case 2:
statusString = "FLASHING"; //2
return statusString;
break;
case 3:
statusString = "SHOWING_BATTERY";//3
case 4:
statusString = "NONE"; //4
return statusString;
break;
default:
return "EnumToString: Invalid enum";
}
}
I would like help fixing the problem, and, more importantly, help understanding the difference between the type char* and the type char.
This line:
char data[20] {EnumToString(currentAction)};
Is wrong because it uses array-initialization to initialize the first element of the array of char (first element which is of type char) with a string (char*). What you want to do is something like this:
char data[20];
strcpy(data, EnumToString(currentAction));
Or simply:
char *data = EnumToString(currentAction);
The latter doesn't involve any kind of copying, and efficiency is important on a micro-controller.
While we're on the point of efficiency, the canonical way of mapping sequential enum values to strings is using an array, which is orders of magnitude more efficient than repeated branching:
// stored as read-only data in the .hex file
static char *names[] = { "BLINKING_RIGHT", "BLINKING_LEFT", "FLASHING", "SHOWING_BATTERY", "NONE" };
// later
const char *data = names[currentAction];
Difference between char and char*
char is a character object. Characters are numbers. The number value encodes some textual character (in fixed width character encodings; A C++ character represents one "code unit" in variable width unicode encoding).
char* is a pointer object. It is specifically a pointer to a char object. A pointer object points to another object in memory - or points to nothing if null or points to memory where an object was if the pointer is invalid. Pointers are also iterators, and incrementing a pointer makes it point to a successive element of an array.
char data[20] {EnumToString(currentAction)}; //This is the line showing the error
data is an array of 20 objects of type char. {expr} initialises the first element of the array with the expression. In your case, the expression is a call to EnumToString. Thus, you're attempting to initialise a char object using the char* object returned by the function. The type system of C++ protects you from this obvious mistake.
Assuming you have the option of sending less than 20 bytes, a potential simple solution is to avoid using the local array entirely:
std::string_view str = EnumToString(currentAction);
rf69.send(str.data(), str.size());
char *statusString;
statusString = "BLINKING_RIGHT"; //0
This is ill-formed in C++. String literals are arrays of const char, and they are not convertible to pointer to non-const char (since C++11).
As simple fix is to change the variable and the return type to be const char*. That said, using std::string_view may be even more preferable because that way the caller doesn't need to calculate the length of the string at runtime by searching for the null terminator.
char (or any T) is a value.
char* (or any T*) is a pointer (a value poining to another value, i.e. address).
Keep in mind what arrays (char[] or any T[]) presented by pointer to 0-th element. Variable of char* can point to an element of an array (to the first element in a special case).
I tried to use a character Pointer go throw string character (iterate) but I found i can not say the below:
string Name = "Hello";
char *ch = Name;
like the previous statements i am getting error during execution.
However when I am doing like that:
char *ch = "Hello";
the program running without throwing any exception.
Why is that?
I have recently encountered similar problem and the simplest answer is that std::string is a different type from char*, more precisely std::string is an object which contains some characters (your text) and few methods, which allow you to do multiple operations with your text. You can imagine creating a class Integer for storing the value, but also a method allowing you to square and cube the number which is stored in the Ingerer class. Even though they could store the same numerical value, you will not be able to compare them (unless you overload the operator==), as their types are different.
If you wish to use the code you provided, you need to rewrite the second line as
const char *ch = Name.c_str();
it is allowed because std::string contains a method c_str() which "casts" itself to const char*. If you want to learn more about strings, be sure to visit C++ reference about strings.
There are some questions on SO that are extremely similar:
How to convert pugi::char_t* to string
I have been struggling with this problem for awhile, and as a beginner I'm really not sure what else to do other than ask here so I am sorry if this counts as duplicate.
pugi::xml_node actors = source_file.child("scene").child("actors");
std::vector<const char*> actor_db;
for (pugi::xml_node actor = actors.first_child(); actor; actor = actor.next_sibling())
{
const char *a = *actor.child_value();
actor_db[actor.attribute("id").as_int()] = a;
}
a value of type "pugi::char_t" cannot be used to initialize an entity of type "const char *"
The line const char *a = *actor.child_value(); is my poor attempt to try to cast the return value of const char_t* xml_node::child_value() as a const char*.
I want the value of actor.child_value() to be stored in a const char* then put inside of the vector actor_db.
pugi::char_t is a type that is either a char or wchar_t depending on the configuration. I have set it up so that pugi::char_t will be a char.
I know this is probably a simple error, but even with my extensive searching and the similar SO questions I am still unable to resolve my issue.
I want the value of actor.child_value() to be stored in a const char* then put inside of the vector actor_db.
No, you don't. The only thing that can be stored "in" a const char* is the location of an array of characters. It's not actually a string, but a pointer to a string. Containers of pointers are fraught with problems.
Store a std::vector<std::string> instead then use the code in the answer1 to the question you linked to in order to obtain the std::strings.
1 Which, oddly enough, I've just noticed I wrote several years ago. :D
I have extracted the contents of a file mapped into memory, into an array which looks like:
char* const arr = static_cast<char *>(file.get_address());
whilst iterating through the array I wish to be able to call:
for(int i=0; i<file.get_size(); i++){
atoi(arr[i]);
}
however, atoi requires type const char* and arr[i] is type char* const. How may I resolve this?
First of all, I question your use of const in...
char* const arr = static_cast<char *>(file.get_address());
In this case, the const applies to the pointer, not to what it points at. So, via your arr pointer, you could do this...
arr[0] = 'a';
...which I think is not what you're trying to protect against. Instead, I think what you really want is:
const char* arr = static_cast<const char *>(file.get_address());
...which resolves what your question was posted about. In this way, you have a pointer that points to characters that can't be changed (at least not directly through that pointer).
Then, in your loop, you're calling atoi() on each character of the whole file, which I doubt is what you really want to do. Are you expecting a whole bunch of single-digit decimal (base 10) numbers, with no separators? That's the only use case for looping the way you are. For the sake of argument, let's suppose that's what you really want. OK, then, where do you want the results to go? You're not assigning the returned value of atoi() to anything. You're converting the single-digit (presumably ASCII) numbers from text into numeric form, but throwing away the results.
But I think that's probably not what you really want anyway.
Let's rewrite your code so that it will convert the file's first textual value (assuming it's not preceded by garbage) into an integer, and then print it. As in your example, we'll assume the file is already read into some object named file and that we can get its data buffer by calling file.get_address(). Here's what you'd want:
const char* arr = static_cast<const char *>(file.get_address());
int firstNumericValue = atoi(arr);
std::cout << "firstNumericValue = " << firstNumericValue << "\n";
...and that's all, no looping required! If you want to read in multiple values from the file, of course you could use looping, but you'll want to look into more advanced functions, such as sscanf() or strtol(). If you use strtol(), its second argument lets you get a pointer to the next place to begin converting for any subsequent calls. But examples for these abound, and you can research them yourself.
atoi(&(arr[i]));
will do the trick.
The & operator gives you a pointer to the char and the extra parentheses ensure that you are getting a pointer to the i-th element.
atoi doesn't care about the const on the array declaration, so it isn't relevant here.
I'm making an unmanaged C++ DLL which uses the C# managed DLL. I'm writting the C++ library as I need to use functions and headers defined in a software for which the C++ library can be added as an addon. But the things I want to make are so complex that my sparse knowledge of C++ would slow me down so I decided to do things in my favourite C# and connect the DLLs via COM and I was successful.
I'm somehow successful in making the code work, but less successful in keeping the code concise as I'm clearly not a professional C++ programmer.
The problem is with converting various string types. BSTR and const char * in particular.
The following code converst const char * to BSTR:
BSTR bstrt;
const char * someChar;
csharpInterfacedClassPointer->get_PropertyForSomeChars(&bstrt);
strcpy_s(nstring, (char *)bstrt);
someChar = nstring;
The problem is, I have plenty of discrete someChars with corresponding discrete interface methods...the property method is generated from the C# interface so I can't change it. Each of the "someChar" requires the following three lines of code so for 30 discrete variables, I'd need to write 90 lines of code.
csharpInterfacedClassPointer->get_PropertyForSomeCharX(&bstrt);
strcpy_s(nstring, (char *)bstrt);
someCharX = nstring;
The question is: how do write some shortcut for this so it'd fit just in one line?
I tried some sort of function with the "getter" function pointer and the someChar pointer.
typedef HRESULT (__stdcall *get_string_func)(BSTR * str); //getter function pointer
//the converting function
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
const size_t nsize = 1000;
char nstring[nsize];
BSTR bstrt;
bstrt = bstr_t(constCharString);
bstr_get_fx(&bstrt);
strcpy_s(nstring, (char *)bstrt);
constCharString = nstring;
}
//calling the function...as I thought that would work
ConvertAndAssign(sPtr->get_DataFile, someChar);
But then the compiler says some weird things aboud bound functions and how they are not allowed as pointers...I googled what does it mean and the solutions given required to alter the function definition but I can't do that since the definition is generated from the C# code (by regasm.exe).
Important note: I need to get the const char * type in the end because it is the required input type to the functions of the program for which I'm making the C++ DLL.
Disclaimer: it was a long time (7 years to be more precise) since I have touched C++/COM code for the last time.
Regarding binding an instance method to a function pointer check this SO question.
Another option is to use the IDispatch interface (if your COM component implement it)
Regarding ConvertAndAssign() implementation, IMO it has some issues. In order to make it easier to explain I have copied it bellow:
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
const size_t nsize = 1000;
char nstring[nsize];
BSTR bstrt;
bstrt = bstr_t(constCharString); // issue 1
bstr_get_fx(&bstrt);
strcpy_s(nstring, (char *)bstrt); // issue 2
constCharString = nstring; // issues 3 & 4
}
if your COM method returns a BSTR (i.e, it has an out parameter of type BSTR) you should not pass on a pre-allocated string otherwise you'll end up
leaking memory.
Casting bstr to char * will not work. BSTRs are Unicode strings. Also they are encoded such its length preceeds the actual characters.
If you are using ATL/MFC you can use one of the string conversion macros.
If you are NOT using ATL/MFC you can use WideCharToMultiByte() function or one of the "Smart" BSTR classes (CComBSTR in ATL, bstr_t, etc)
Assigning nstring to constCharString will have no effect on the outside string. If you are calling ConvertAndAssign like follows
char *outsideStr = NULL;
ConvertAndAssign(whatever, outsideStr);
Inside ConvertAndAssign() function consCharString will point to NULL in the begning. After the assignment constCharString does point to nstring but
outsideStr still points to NULL (remember, when you called ConvertAndAssign() function a copy of the pointer value was passed to it).
In order to get what you want you can either pass a reference or a pointer to a pointer:
void ConvertAndAssign(get_string_func bstr_get_fx, const char * &constCharString)
{
constCharString = nstring; // Assignment
}
or a pointer to a pointer:
char *outsideStr = NULL;
ConvertAndAssign(whatever, &outsideStr);
void ConvertAndAssign(get_string_func bstr_get_fx, const char **constCharString)
{
.
.
.
*constCharString = nstring; // Assignment
}
After fixing the previous issue you'll hit another one: You cannot return the address of a local variable! When your code resumes after ConvertAndAssign() returns, this
address is not allocated for you anymore (it's part of the stack, so it may even look to be working, but I assure you, it is not; the slightest changes
in your code may break it)
To fix this you need to pass a pre-allocated string:
char outsideStr[1000];
ConvertAndAssign(whatever, outsideStr);
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
strcpy_s(constCharString, /* result str here */ );
}
or allocate a string in the heap.
Given all the above, one possible implementation for ConvertAndAssign() and its usage is as follow:
char outsideStr[1000];
ConvertAndAssign(whatever, outsideStr);
void ConvertAndAssign(get_string_func bstr_get_fx, const char * constCharString)
{
BSTR bstrt;
if(SUCCEEDED(bstr_get_fx(&bstrt)))
{
// Assumes constCharString points to a buffer large enough to hold the converted string.
strcpy_s(constCharString, CW2A(bstr)); // not completely correct since bstr may contain the byte 0 but I guess thats not your scenario.
SysFreeString(bstr);
}
else
{
// error handling.
constCharString[0] = 0;
}
}
This question can be deleted if any mod is going to read this. I realized I have a totally different problem. Thanks Vagaus for his effort.