I have this Rust function:
pub extern "C" fn do_something(my_string: &str) {
let s = String::from(my_string);
}
That I call on C++ with this:
std::string my_string("hello");
do_something(my_string.c_str());
Signature:
extern "C" void* do_something(const char*);
I get this error right on the String::from line:
memory allocation of 127963177044160 bytes failedAborted (core dumped)
I guess that it's because the string passed has no \n so it tries to make a string of the maximum size possible.
How to safely pass a std::string to Rust?
That I call on C++ with this:
do_something(my_string.c_str());
So on the C++ side you're calling a function with a C string as input (not an std::string, which is a very relevant distinction).
This means the Rust function should take a C string as input, which &str definitely isn't.
Thus do_something should be declared as:
pub extern "C" fn do_something(my_string: *const c_char) {
following which as Jmb notes you may want to use CStr in order to safely wrap the pointer.
The &str type is not FFI-safe. I would expect the Rust compiler to issue a warning to that effect. Rust slices consist of a pointer and a length and do not have a layout compatible with the C++ const char*.
One option would be to have do_something accept a pointer and a length (*const u8 and usize, respectively), call std::slice::from_raw_parts to exchange those for a &'a [u8], and call std::str::from_utf8_unchecked to exchange that for a &str. You must ensure the safety conditions documented along with each of those functions are upheld, including that the string contains valid UTF-8.
Related
I am now using C++ to program a robot using PROS. Pros has a print function, which is taking in a const char*. Now, I'm using lvgl to create my own screen, and I want to replicate the print function. Like the printf() functions, I want it to include variadic params to do the %d effect (so it converts all the %? to the corresponding values). The problem now is about the conversions between functions. I wanted to make a convert function to convert a string and the variadic params into a complete string. I need to input is a string which is like "hey" and I'm unsure what the type name should be. I need to be able to get size, search in it for %ds but I need the function to return a const char* to pass onto the lvgl to pring on the screen. I am having a bad time trying to convert a string into an const char* for the out put of the convert function.
Also, I tried using the input type as a char*, and when I input a string like "hello" is says a error [ISO C++11 does not allow conversion from string literal to 'char ' [-Wwritable-strings]]. But instead, when is use a const char, the error disappears. Anyone knows why?
Thanks everyone for your kind help!
char* and const char* are two flavours of the same thing: C-style strings. These are a series of bytes with a NUL terminator (0-byte). To use these you need to use the C library functions like strdup, strlen and so on. These must be used very carefully as missing out on the terminator, which is all too easy to do by accident, can result in huge problems in the form of buffer-overflow bugs.
std::string is how strings are represented in C++. They're a lot more capable, they can support "wide" characters, or variable length character sets like UTF-8. As there's no NUL terminator in these, they can't be overflowed and are really quite safe to use. Memory allocation is handled by the Standard Library without you having to pay much attention to it.
You can convert back and forth as necessary, but it's usually best to stick to std::string inside of C++ as much as you can.
To convert from C++ to C:
std::string cppstring("test");
const char* c_string = cppstring.c_str();
To convert from C to C++:
const char* c_string = "test";
std::string cppstring(c_string);
Note you can convert from char* (mutable) to const char* (immutable) but not in reverse. Sometimes things are flagged const because you're not allowed to change them, or that changing them would cause huge problems.
You don't really have to "convert" though, you just use char* as you would const char*.
std::string A = "hello"; //< assignment from char* to string
const char* const B = A.c_str(); //< call c_str() method to access the C string
std::string C = B; //< assignment works just fine (with allocation though!)
printf("%s", C.c_str()); //< pass to printf via %s & c_str() method
I have a function with this signature:
const char* get_version();
My declaration is:
fun get_version(): LibC::Char*
And to use it:
version = MyLib.get_version()
puts version # how to convert to String?
How can I manage the return string? Do I have to import also strlen to measure the length of the C string and covert it to Crystal string manually?
You wrap the Char* pointer with String.new(MyLib.version). If you know the string length, you may call String.new(ptr, size) too.
Note that this will copy the data from the pointer, so the C binding may release its pointer without affecting the Crystal String.
class Student {
public:
string name;
};
vs
class Student {
public:
char* name;
};
Please correct me if I'm wrong. If we were to use char* instead of string, we will have to write our very own copy-constructor because we need to every time we have pointer variables as data members. Right?
So, my question is: Why use char* at all?
Using string, in the constructor, we can directly do:
Student(string s) {
name = s;
}
which is simpler compared to char*, which needs:
Student(string s) {
name = new char[strlen(s)+1]; // extra 1 to store the '\n'
strcpy(name,s);
}
Why not use string at all times instead of char* when being used as a data member of a class?
I think the only reason char* is used in C++ as a string is because of C. I'm sure if it was a new language, one which didn't strive to be compatible with C, char* would not be used like that. You will notice that functions that handle char* as if it were a string all come from C.
Note that in C, there is no string, so
struct Student { char* name; };
is perfectly valid C code, whereas
struct Student { string name; };
is not. Therefore, it is not unusual, when dealing with code which previously target C, to see those char* types.
There are usually little reason for using char* as a string, unless you are either writing a new string class, interfacing C functions, or dealing with legacy code.
You use char * instead of string, because a string is a string and a char * is a pointer to a character-aligned address.
Expanding on that, a string is an abstraction of a vector of characters with defined semantics. In C land, and in a lot of C++ programs, it represents an allocated block of memory along with a guarantee that it's terminated with the ascii NUL character 0x00. But a C++ implementation of string could instead use, say, a Pascal string with associated length, or it could represent strings in a string pool as a linked list.
A char * isn't providing that guarantee at all, and in fact might not be a string -- for example, it might be a collection of data with embedded 0x00 values. All it promises is that it's an address of something that the underlying architecture thinks is a character.
If you need a string, use std::string, not char*.
An obvious exception is interfacing to legacy code that uses char* to represent strings. Still, don't use char* outside of the interface layer.
You need to use char* when your data isn't a string, but a raw unstructured array of bytes. This is the case when you read or write binary data from files or network interfaces.
Sometimes, it is simpler to use char* instead of string, for example, when you are dealing with network and you need to transform a string full of bytes into a integer, float, etc.. I think that it's simpler to use a char*, instead of a string :
Using a char*
char* buffer[4];
read(buffer, 4); // A random read operation
int value = *((int*)(&buffer[0]); // Not sure if it was like that...
Using a string
std::string buffer;
buffer.resize(4);
read(buffer.data(), 4); // Will not work as buffer.data() returns a const char*
int value = *((int*)(&buffer.data()[0]));
The problem of string is that it's designed to prevent bad usage or strange manipulations. As someone said, it's also because C++ is inherited from C. So there is functions (from libc/glibc) which takes a char* and not a string.
EDIT
Even if char* is different from char**, it's pretty complex to build a bi-dimensional array using std::vector or std::string, you should either make your proper class, use char**, or library specific implementation (Boost, Maths libs, etc...)
About the only place where a competent C++ programmer will use char* is in the interface of an extern "C" program, or in very low level code, like an implementation of malloc (where you need to add a number of bytes to a void*). Even when calling into a C ABI, the char* needed by the interface will generally come from a &s[0], where s is an std::string, or if the interface is not const aware (and a lot of C interfaces aren't), then the results of a const_cast.
char const* is a bit more frequent: a string literal is, after all, a char const[], and I will occasionally define something like:
struct S
{
int value;
char const* name;
};
But only for static data, eg:
S const table[] =
{
{ 1, "one" },
{ 2, "two" },
// ...
};
This can be used to avoid order of initialization issues; in the above, the initialization is static, and guaranteed to take place before any dynamic initialization.
There are few other cases: I've used char const*, for example, when marshalling between to C ABIs. But they are rare.
Old library was written in Delphi. Now I'm trying to write library in c++.
Below there is function in Delphi's library:
function MyFunction(Path:string; Options:PInteger; var Data: array of Byte):Integer; stdcall;
How should this function look in C++? Is below declariation right?
int __stdcall MyFunction(char* Path, int* Options, char* Data);
The big problem is the buffer. Your Delphi function passes an open array. This is implemented by passing both the array length, and the pointer to the first element. Your C++ translation does not do that. You'll need to pass an extra parameter specifying the length of the array.
Since you can expect that the function will not modify Path you would likely declare the first parameter as const char*.
So, the function would then look like this:
int __stdcall MyFunction(const char* Path, int* Options, size_t len, char* Data);
Now, if you are expecting that the function is to be binary interchangeable with the original C++ version you have a problem. You'd need to match the internal implementation of a Delphi open array. You can do that. The function would become:
int __stdcall MyFunction(const char* Path, int* Options, char* Data, int high);
Note that the length parameter appears after the pointer to the first element, and is named high. That's because a Delphi open array receives high(A) rather than Length(A).
You really must get out of the habit of passing Delphi specific types across interop boundaries.
Of course, I'm assuming here that you are still creating a library for interop and this function is at the interop boundary. If the function is internal then the declaration would perhaps be:
void MyFunction(const std::string &path, int &options,
const std::vector<char> &data);
There's no need for a return value since errors can be signalled with exceptions. Strings are stored in std::string, and a byte array in C++ is std::vector<char>.
I have the function below in a file called WiServer.h for Arduino.
GETrequest(uint8* ipAddr, int port, char* hostName, char* URL);
Now the problem is I need to concatenate an int value (setting1) to the char* URL parameter like the below for example.
"twittermood.php?status=sendTweet&setting1="+setting1
I get an error:
invalid conversion from const char* to char*
How do I fix it?
You've gotten decent generic C++ advice, but for the special case of Arduino on an 8-bit AVR microcontroller I think you need platform-specific advice:
The Arduino runtime provides a String object. Your question is probably covered by these examples.
Because of the very limited RAM space on Arduino it is common to use special attributes to put constant strings in flash (essentially ROM) which requires different instructions to access. AVR code built with GCC is typically built on top of AVR Libc which has support for operating on a mix of constant strings and RAM strings. You must be explicit in your code and choose the right operations. This requires at least a basic understanding of how C strings work and how pointers work. I'm not sure how much of this cleverness is automatically provided by the Arduino String, but without this cleverness all of your string constants will end up copied into RAM at boot and will take up those precious bytes all the time.
If RAM space becomes a problem or you are working on an AVR application that does extensive string manipulation you need to learn how to use the mix of PGM Space operations (string functions that can work on read-only strings in flash) and regular C-style RAM-based string operations.
Use std::string, rather than C strings. Use string streams, rather than trying to concatenate non-string values to strings:
std::ostringstream oss;
oss << "twittermood.php?status=sendTweet&setting1=" << setting1;
use(oss.str()); // or use(oss.str().c_str());
If that API really needs a non-const string (given that it doesn't even take the length of the string, I suppose it's just a buggy API disregarding const), copy the string to a buffer and pass that:
const std::string& str = oss.str();
std::vector<char> buffer(str.begin(), str.end());
buffer.push_back('\0');
GETrequest(addr, port, &buffer[0], c);
As for what really happens when you do what you do:
"twittermood.php?status=sendTweet&setting1=" is an rvalue of the type char[43], which implicitly converts to const char*, a pointer to the first character. To that you add an integer, by this forming a new pointer of the type const char* pointing to some more or less random memory location. I suppose you try to pass this as the char* to your API function, for which the const would have to be dropped.
A C++ compiler, however, will never implicitly drop a const — for your own good.
Use a std::string, not a char*, for this sort of work. A char* in C is extremely basic and if you're not familiar with how C works, very easy to use wrong.
If you need to use char*, look into strcpy, strcat and snprintf. But these functions are very dangerous in a novice's hands and can lead to memory corruption and crashing.
You can use an ostringstream for this:
#include <sstream>
// ...
std::ostringstream os;
os << "twittermood.php?status=sendTweet&setting1=" << setting1;
GETrequest(addr, port, hostname, os.str().c_str());
Use std::string instead of char* and maybe a std::stringstream for your concatination. But first about your errors:
Your problem is that "twittermood.php?status=sendTweet&setting1=" will get you a const char*, which can't be implicitely converted to a char*. If you are really sure that GETrequest doesn't try to change the value of its URL parameter, you can use const_cast<char*>(...) on your const char* variable to cast away the constness. However, do this only if you are absolutely sure it won't be changed (don't lie to the compiler about constness (or anything really)).
Even if you do that "twittermood.php?status=sendTweet&setting1="+setting1 won't do what you think it does. As I said your string constant will give you a const char*, which doesn't have any knowledge about string operations. So adding an intto it won't concat that int to the string, but instead do some pointerarithmetic, so if you are lucky and your int was small enough you get only a part of the URL, otherwise you will address something completely different.
Posting C solution for completeness:
const char ctext[] = "twittermood.php?status=sendTweet&setting1=";
char text[sizeof(ctext) + 20];
snprintf(text, sizeof(text), "%s%i", ctext, setting1);
std strings and streams are much nicer/safer to use.