Change uint8_t* to char*? - c++

I have an API which requests a char*, this is my API function:
CANMessage(unsigned _id, const char* _data, char _len = 8)
More information available here: https://os.mbed.com/docs/mbed-os/v5.11/mbed-os-api-doxy/classmbed_1_1_c_a_n_message.html
I would like to call this function from within another function, but I am getting confused about const char* and casting. I want to call this function from function foo(), like so:
void foo(unsigned int id, /*???*/ data, char len) {
CANMessage(id, data, len)
}
So I need to pass id, data and len to function foo. My problem is that the data coming in is a uint8_t type. I got a vector of uint8_t, where the address of the first element is the one I need to pass:
vector<uint8_t> dta;
Which I tried to pass as &dta[0]: foo(idNo, &dta[0], length)
With the foo function as so:
void foo(unsigned int id, uint8_t* data, char len) {
CANMessage(id, (char*)data, len)
}
But I get "Argument of type std::uint8_t * is incompatible with parameter of type char*
How do I pass it as const char* when function foo, which calls it, accepts uint8_t*?
Please note I can't change types, dta has to stay vector<uint8_t>.

std::uint8_t ιs equal to unsigned char.
This is different from plain char or signed char, but all of them are 8 bit, therefore casting would techically work.
It's common that many functions that would otherwise need a "buffer" have a char* in their definition instead of the proper unsigned char*. Therefore, casting would most probably be harmless.
In the case that the function actually wants characters but not a buffer, then you have a problem because the types are different, and whether you will have an issue or not is undefined.

Since you are in an environment where std::uint8_t is available, the char types must be max 8 bits, but just to make sure you're not on a machine with 7 bit char's, add a static_assert.
reinterpret_cast the uint8_t* to const char* and static_cast the size (size_t) of the vector to char.
void foo(unsigned _id, const std::vector<uint8_t>& dta) {
static_assert(CHAR_BIT == 8, "Strange char");
CANMessage(
_id,
reinterpret_cast<const char*>(dta.data()),
static_cast<char>(dta.size())
);
}

Related

How to convert string to const unsigned char* without using reinterpret_cast (modern approach)

I have variable input type const std::string&:
const std::string& input
Now I need to convert this to const unsigned char* because this is the input of the function.
Unitl now I have correct code for converting:
reinterpret_cast<const unsigned char*>(input.c_str())
This works well, but in clang I got a warning:
do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
What is the correct way to change a string or const char* to const unsigned char*?
What is the correct way to change a string or const char* to const unsigned char*?
The correct way is to use reinterpret_cast.
If you want to avoid reinterpret_cast, then you must avoid the pointer conversion entirely, which is only possible by solving the XY-problem. Some options:
You could use std::basic_string<unsigned char> in the first place.
If you only need an iterator to unsigned char and not necessarily a pointer, then you could use std::ranges::views::transform which uses static cast for each element.
You could change the function that expects unsigned char* to accept char* instead.
If you cannot change the type of input and do need a unsigned char* and you still must avoid reinterpret cast, then you could create the std::basic_string<unsigned char> from the input using the transform view. But this has potential overhead, so consider whether avoiding reinterpret_cast is worth it.
Edit
Apparently type punning with an union is UB so definitely don't do this.
(Keeping the answer for posterity though!)
To strictly answer your question, there's this way:
void foo(const unsigned char* str) {
std::cout << str << std::endl;
}
int main()
{
std::string word = "test";
//foo(word.data()); fails
union { const char* ccptr; const unsigned char* cucptr; } uword;
uword.ccptr = word.data();
foo(uword.cucptr);
}
Is this any better than a reinterpret_cast? Probably not.

If buffer of bytes should be unsigned char do I have to keep casting all the time?

According to these answers a buffer of bytes should be unsigned char, either because of convention or maybe the padding guarantees, I'm not sure. I have a function that looks something like:
saveDataToFile(const unsigned char* data, size_t size);
I find that I keep having to cast when I have a vector of char or an std::string or a string literal or something, and my code ends up looking like:
const char* text = "text";
saveDataToFile(text, 4); // Argument of const char* is incompatible with parameter of type const unsigned char*
saveDataToFile(reinterpret_cast<const unsigned char*>(text), 4);
Is there a way to avoid doing this all the time? Someone once mentioned to make my function take const char* instead of unsigned, but that doesn't really as then I'd have to cast the other way. For example std::string has .c_str() and .data() that return signed and unsigned. I also thought about taking void*, maybe that's the best way?
Perhaps the simplest way, as you have suggested yourself, is to make the function's first argument a const void* and then cast that to whatever is needed inside the function. This way, you also avoid using a reinterpret_cast and can safely use a static_cast:
void saveDataToFile(const void* data, size_t size)
{
const uint8_t* local = static_cast<const uint8_t*>(data);
//.. do something with the cast pointer ...
}
int main()
{
double dData = 33.3;
int16_t sData = 42;
char cData[] = "Hello, World!";
saveDataToFile(&dData, sizeof(dData));
saveDataToFile(&sData, sizeof(sData));
saveDataToFile(cData, sizeof(cData));
return 0;
}
A more "Pure C++" way (in some folks' eyes, maybe) would be to make a templated function. However, the disadvantages here are: (a) you will need a reinterpret_cast in this case; and (b) the compiler will (probably) generate separate function code for each of the different argument types used:
template<typename T>
void saveDataToFile(const T* data, size_t size)
{
const uint8_t* local = reinterpret_cast<const uint8_t*>(data);
//.. do something with the cast pointer ...
}

Taking an index out of const char* argument

I have the following code:
int some_array[256] = { ... };
int do_stuff(const char* str)
{
int index = *str;
return some_array[index];
}
Apparently the above code causes a bug in some platforms, because *str can in fact be negative.
So I thought of two possible solutions:
Casting the value on assignment (unsigned int index = (unsigned char)*str;).
Passing const unsigned char* instead.
Edit: The rest of this question did not get a treatment, so I moved it to a new thread.
The signedness of char is indeed platform-dependent, but what you do know is that there are as many values of char as there are of unsigned char, and the conversion is injective. So you can absolutely cast the value to associate a lookup index with each character:
unsigned char idx = *str;
return arr[idx];
You should of course make sure that the arr has at least UCHAR_MAX + 1 elements. (This may cause hilarious edge cases when sizeof(unsigned long long int) == 1, which is fortunately rare.)
Characters are allowed to be signed or unsigned, depending on the platform. An assumption of unsigned range is what causes your bug.
Your do_stuff code does not treat const char* as a string representation. It uses it as a sequence of byte-sized indexes into a look-up table. Therefore, there is nothing wrong with forcing unsigned char type on the characters of your string inside do_stuff (i.e. use your solution #1). This keeps re-interpretation of char as an index localized to the implementation of do_stuff function.
Of course, this assumes that other parts of your code do treat str as a C string.

How to use C functions in Qt C++

I am a web developer and I am new to C++. I am using Qt C++. I was looking a way to generate a PBKDF2 key in Qt, but could not find a way to do that in pure C++. So looking on internet I have found this small C implementation https://github.com/ctz/fastpbkdf2. I need to use the following function
void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
const uint8_t *salt, size_t nsalt,
uint32_t iterations,
uint8_t *out, size_t nout)
In my C++ file, I have
QString password = "password";
QString salt = "salt";
int iterations = 30000;
I know I can directly call any C function in C++, but I am not sure about how can I call that function with those parameters from my C++ file. An explanation of data type conversions would also be appreciated.
All you need to convert QString to char (or uint8_t):
QString passoword = "password";
QByteArray ba = password.toLatin1();
const uint8_t *pw = (const uint8_t*)ba.data();
And you can use this pw in the function. The same for "salt". You can use "iterations" as it is. For "out" parameter, allocate uint8_t with any method you prefer, could be malloc.
uint8_t is a typedef for an unsigned 8-bit integer, or in other words an unsigned char.
uint32_t is a typedef for an unsigned 32-bit integer, or in other words an unsigned int.
These typedefs are defined in stdint.h. These types were introduced to have well-defined (width-wise) integer types for portability.
size_t is typically an unsigned integer type as well. So the
prototype:
void fastpbkdf2_hmac_sha256(const uint8_t *pw, size_t npw,
const uint8_t *salt, size_t nsalt,
uint32_t iterations,
uint8_t *out, size_t nout)
is equivalent to:
void fastpbkdf2_hmac_sha256(const unsigned char* pw, unsigned int npw,
const unsigned char* salt, unsigned int nsalt,
unsigned int iterations,
unsigned char* out, unsigned int nout);
Others have posted how to convert QString into a unsigned character array.
I am not too familiar with QT so I am looking at this page as a reference for a QString. It appears to be a managed string object while the function you want to call just wants a null terminated array of uint8_ts. I think it is wrapping std::string because there is a toStdString() member function. A std::string just manages a character array which is what your function wants so you can do this: password.toStdString().c_str() to get the char *.
Most platforms will implicitly convert uint8_t and char since they are almost always straight up 1 byte of memory.
There is also an output buffer and by the calling convention it looks like you have to manage that memory. From glancing at the docs of the github you linked it will output however much memory you tell it to using nout. In this example we create a 256 byte buffer for it to output to.
QString password = "password";
QString salt = "salt";
int iterations = 30000;
const size_t nout = 256;
uint8_t * out = new uint8_t[nout];
fastpbkdf2_hmac_sha256(password.toStdString().c_str(), password.toStdString().size(),
salt.toStdString().c_str(), salt.toStdString().size(),
iterations,
out, nout);
// use out
delete[] out;

Array passing to functions differences

I want to ask if there is difference between this two array passing method:
unsigned char array[100];
function(array);
Where:
library.cpp
uint8_t LibraryClass::function(unsigned char array[]) { }
library.h
uint8_t function(unsigned char array[]);
And this:
unsigned char array[100];
function(array);
Where:
library.cpp
uint8_t LibraryClass::function(const unsigned char* array) { }
library.h
uint8_t function(const unsigned char* array);
My questions is:
There is difference between this to methods?
Additional question:
My MCU need to do additional operation in method 1 instead of method 2?
There is additional const in method 2, why is used? It is safety to use const when using pointers?
unsigned char array[] is just syntactic sugar for unsigned char *array in a function declaration. They're literally identical.
The const means that function guarantees not to modify the contents of array. The first example makes no such guarantee to the caller.
Normally in c/c++ unsigned char array[] is unsigned char* array which consists of contiguous memory.
Whenever an array is passed to a function it is treated as pointer(*) denoting the base address.
Mentioning const- (const unsigned char* array) denotes its value will not change.