I'm here looking at some C++ code and am not understanding something. It is irrelevant but it comes from a YARP (robot middleware) tutorial which goes with the documentation.
virtual void getHeader(const Bytes& header)
{
const char *target = "HUMANITY";
for (int i=0; i<8 && i<header.length(); i++)
{
header.get()[i] = target[i];
}
}
Now, header is a reference to const and thus cannot be modified within this function. get is called on it, its prototype is char *get() const;. How can header.get() be subscripted and modified ? The program compiles fine. I may have not understood what happens here but I'm basing myself on what I've read in C++ Primer...
I would very much appreciate a little clarification!
Have a nice day,
char *get() const;
The right hand const means "this member doesn't alter anything in the class that's not mutable", and it's honoring that - it isn't changing anything. The implementation is probably something like this:
char *Bytes::get() const
{
return const_cast<char *>(m_bytes);
}
The pointer that is being returned, however, is a simple "char*". Think of it this way:
(header.get())[i] = target[i];
// or
char* p = header.get();
p[i] = target[i];
Whoever designed the interface decided that the content of a const Byte object can be modified by stuffing values into it. Presumably they've done whatever hacks they needed to make header.get()[i] modifiable. I wouldn't use this code as an exemplar of good interface design.
Looking at the doc:
struct Bytes {
char* get() const; // works
char*& get() const; // would not work
char* mem_;
};
This code is perfectly valid, even though it is bad practice. The
problem is that a copy of the pointer is made and the constness of the
class is lost. constness in C++ is largely conceptual and easy to
break (often even without consequences). I'd complain to the
implementer. It should look like this:
struct Bytes {
char* get(); // works
const char* get() const; // would not work
char* mem_;
};
header.get() should returns char*, assuming it as base address and indexed with [i] and string in target coped to that location.
#antitrust given good point, return address can't be modified by address content can e.g.
char x[100];
char* get() const
{
return x;
}
int calling function you can do like:
get()[i] = target[i];
it will copy target string to x, this method can be useful when x is private member to class, and you are to copy in x.
Edit if get() is a inline function then calling get() function in a loop will not effect performance., I mean such function should be defined inline.
Related
I'm currently working on a project where I often have to build linked lists of various C structs. Since I don't want to keep repeating myself setting next pointers, I wrote some helper templates, but soon found out that it falls apart if one of the next fields is a pointer-to-const.
My linked list elements look something like this:
struct WorkingElementType {
void *pNext;
/* stuff */
};
struct TroublesomeElementType {
const void *pNext;
/* stuff */
};
In reality, there are of course a lot more of these structs. My helper functions have to keep a pointer to the last element's pNext field in order to write to it when the linked list gets extended, so I went for a void **ppNext = &last->pNext. Unfortunately, that of course breaks down with TroublesomeElementType and its const void *pNext.
In the end, what I'd like to achieve is this:
void **m_ppNext;
/* In one function */
m_ppNext = &last->pNext;
/* In a different function, extending the list */
T *elementToAppend = ...;
*m_ppNext = elementToAppend;
I solved this by using a std::variant<void **, const void **> ppNext instead, but using a std::variant and std::visit just for a difference in constness that doesn't even affect the code's function feels like a bit of a waste.
That's why I'm wondering: Is it legal to use const_cast here to cast away const and stuff the const void ** into a void ** only for updating the pointed-to pointer? No const object actually gets modified, after all.
In other words: I'm not sure whether it's legal to alias const void* and void *. (My gut feeling says no, it's not legal because these are incompatible types, but I don't know for sure.)
The C++ standard in question is C++20.
Here's some simple example code:
#include <variant>
int g_i = 42;
/* This is legal */
void setIntPtr1(std::variant<int **, const int **> v) {
std::visit([](auto& p) { *p = &g_i; }, v);
}
int testNonConst1() {
int *i;
setIntPtr1(&i);
return *i;
}
int testConst1() {
const int *i;
setIntPtr1(&i);
return *i;
}
/* But I'm not sure about this */
void setIntPtr2(int **p) {
*p = &g_i;
}
int testNonConst2() {
int *i;
setIntPtr2(&i);
return *i;
}
int testConst2() {
const int *i;
setIntPtr2(const_cast<int **>(&i)); // Is this legal?
return *i;
}
On Godbolt, all of the various test... functions compile to the exact same assembly, but I don't know if testConst2 is legal C++.
I've found the following two existing questions:
Is it legal to modify any data pointer through a void **
Why isn't it legal to convert "pointer to pointer to non-const" to a "pointer to pointer to const"
However, both of them don't seem to quite answer my question. The first one deals with casting any T** to a void **, which is not what I'm doing; I'm just casting away constness. The second one asks why it's a compile error to convert a void ** to a const void **, but not whether interpreting the memory of a void * as a const void * and vice-versa (without actually overwriting a const object) would be a violation of the aliasing rules.
Yes, it is legal.
[basic.lval]/11:
If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
the dynamic type of the object [...]
T* and const T* are similar:
Two types T1 and T2 are similar if they have cv-decompositions with the same n such that corresponding Pi components are either the same or one is "array of Ni" and the other is "array of unknown bound of", and the types denoted by U are the same.
I want to write portable code in c++11 for different CPU's (actually MCU's). As some CPU's does not support reading program data directly through it's memory address space (such as Atmel AVR), I need a solution that calls a function either with a direct address, or with a custom made Stream pointer to read the data trough some external storage.
Consider this code as the custom library:
class IStream
{
public: virtual char ReadChar();
};
class ConstMemoryStream : public IStream
{
const char* Position;
public: ConstMemoryStream(const char* startAddress)
{
Position = startAddress;
}
public: char ReadChar() override
{
return *Position++;
}
};
void Send(char data) { } // Send data to serial port
Now, I want to implement a function that takes either a memory address, or a Stream to read the data from:
// const parameter is needed here, otherwise error: invalid initialisation of non-const reference of type 'IStream&' from an rvalue of type 'IStream'
void PrintMessage(const IStream& stream)
{
while (true) // TODO: end condition
//Send(stream.ReadChar()); // this gives an error because i need to use a const parameter: passing 'const IStream' as 'this' argument discards qualifiers
Send( ((IStream*)&stream)->ReadChar() ); // this works, this actually bypass the error above. IS THIS OK?????
}
void PrintMessage(char* address); // overload to use memory instead of stream. implementation not important here
Next, I want to call PrintMessage with a Stream, but this stream needs to be created inline, and is not needed anymore outside the PrintMessage function:
int main(void)
{
// Requirement: ConstMemoryStream needs to be created and passed INLINE PrintMessage
PrintMessage(ConstMemoryStream("Hello!")); // This works only if i put const in PrintMessage parameter.
}
All the code above compiles and works, but my main concern is that I need to use a const parameter in the PrintMessage function (otherwise I get an error). Because of this, I need to do an ugly cast:
Send( ((IStream*)&stream)->ReadChar() );
This basically makes the parameter non-const to avoid the error. But is there a better solution to do this "legally"?
The stream instance itself cannot be const because it advances it's position internally, but c++ requires to pass it as const because it's an inline temporary variable which is always considered as an rvalue.
I don't see any harm from a temporary variable to modify itself, after the PrintMessage function return it's discarded anyway.
The inline requireWhat I finally want to do is this:
#ifdef CPU_AVR
#define CSTR(str) ConstMemoryStream(PROGMEM str) // the PROGMEM attribute puts the text in a separate space not accessible in regular memory
#elif defined CPU_SAM
#define CSTR(str) (char*)str
#endif
int main2(void)
{
// If the CPU does not support direct address mapping to it's FLASH space, pass a stream instead of a direct memory pointer
PrintMessage(CSTR("Hello"));
}
Any idea on how to do this properly without casting to discard the error? Or is the current code above acceptable?
With C++11 you can simply take an rvalue reference as a parameter.
void PrintMessage(IStream && stream)
rvalue references will bind to temporaries, and will mostly be indistinguishable from lvalue references, in this context.
Either your interface is wrong, or your usage is wrong. Currently you are const_casting your stream. If you ever pass a const IStream object, that would be undefined behaviour.
Either:
class IStream
{
public: virtual char ReadChar() const;
};
Or:
void PrintMessage(IStream& stream)
{
while (true) // TODO: end condition
Send(stream.ReadChar());
}
void PrintMessage(IStream&& stream)
{
while (true) // TODO: end condition
Send(stream.ReadChar());
}
int main(void)
{
PrintMessage(ConstMemoryStream("Hello!"));
}
Basically Im wanting to fetch a pointer of a constant and anonymous object, such as an instance of a class, array or struct that is inialised with T {x, y, z...}. Sorry for my poor skills in wording.
The basic code that Im trying to write is as follows:
//Clunky, Im sure there is an inbuilt class that can replace this, any information would be a nice addition
template<class T> class TerminatedArray {
public:
T* children;
int length;
TerminatedArray(const T* children) {
this->children = children;
length = 0;
while ((unsigned long)&children[length] != 0)
length++;
}
TerminatedArray() {
length = 0;
while ((unsigned long)&children[length] != 0)
length++;
}
const T get(int i) {
if (i < 0 || i >= length)
return 0;
return children[i];
}
};
const TerminatedArray<const int> i = (const TerminatedArray<const int>){(const int[]){1,2,3,4,5,6,0}};
class Settings {
public:
struct Option {
const char* name;
};
struct Directory {
const char* name;
TerminatedArray<const int> const children;
};
const Directory* baseDir;
const TerminatedArray<const Option>* options;
Settings(const Directory* _baseDir, const TerminatedArray<const Option> *_options);
};
//in some init method's:
Settings s = Settings(
&(const Settings::Directory){
"Clock",
(const TerminatedArray<const int>){(const int[]){1,2,0}}
},
&(const TerminatedArray<const Settings::Option>){(const Settings::Option[]){
{"testFoo"},
{"foofoo"},
0
}}
);
The code that I refer to is at the very bottom, the definition of s. I seem to be able to initialize a constant array of integers, but when applying the same technique to classes, it fails with:
error: taking address of temporary [-fpermissive]
I don't even know if C++ supports such things, I want to avoid having to have separate const definitions dirtying and splitting up the code, and instead have them clean and anonymous.
The reason for wanting all these definitions as constants is that Im working on an Arduino project that requires efficient balancing of SRAM to Flash. And I have a lot of Flash to my disposal.
My question is this. How can I declare a constant anonymous class/struct using aggregate initialization?
The direct (and better) equivalent to TerminatedArray is std::initializer_list:
class Settings {
public:
struct Option {
const char* name;
};
struct Directory {
const char* name;
std::initializer_list<const int> const children;
};
const Directory* baseDir;
const std::initializer_list<const Option>* options;
Settings(const Directory& _baseDir, const std::initializer_list<const Option>& _options);
};
//in some init method's:
Settings s = Settings(
{
"Clock",
{1,2,0}
},
{
{"testFoo"},
{"foofoo"}
}
);
https://godbolt.org/z/8t7j0f
However, this will almost certainly have lifetime issues (which the compiler tried to warn you about with "taking address of temporary"). If you want to store a (non-owning) pointer (or reference) then somebody else should have ownership of the object. But when initializing with temporary objects like this, nobody else does. The temporaries die at the end of the full expression, so your stored pointers now point to dead objects. Fixing this is a different matter (possibly making your requirements conflicting).
Somewhat relatedly, I'm not sure whether storing a std::initializer_list as class member is a good idea might. But it's certainly the thing you can use as function parameter to make aggregate initialization nicer.
&children[length] != 0 is still true or UB.
If you don't want to allocate memory, you might take reference to existing array:
class Settings {
public:
struct Option {
const char* name;
};
struct Directory {
const char* name;
std::span<const int> const children;
};
const Directory baseDir;
const std::span<const Option> options;
Settings(Directory baseDir, span<const Option> options);
};
//in some method:
const std::array<int, 3> ints{{1,2,0}};
const std::array<Settings::Option> options{{"testFoo"}, {"foofoo"}};
Settings s{"Clock", {ints}}, options};
First, you're not aggregate-initializing anything. This is uniform initialization and you're calling constructors instead of directly initializing members. This is because your classes have user-defined constructors, and classes with constructors can't be aggregate-initialized.
Second, you're not really able to "initialize a constant array of integers". It merely compiles. Trying to run it gives undefined behavior - in my case, trying to construct i goes into an infinite search for element value 0.
In C++, there's values on the stack, there's values on the heap and there's temporary values (I genuinely apologize to anyone who knows C++ for this statement).
Values on the heap have permanent addresses which you can pass around freely.
Values on the stack have temporary addresses which are valid until
the end of the block.
Temporary values either don't have addresses
(as your compiler warns you) or have a valid address for the duration
of the expression they're used for.
You're using such a temporary to initialize i, and trying to store and use the address of a temporary. This is an error and to fix it you can create your "temporary" array on the stack if you don't plan to use i outside of the block where your array will be.
Or you can create your array on the heap, use its address to initialize i, and remember to explicitly delete your array when you're done with it.
I recommend reading https://isocpp.org/faq and getting familiar with lifetime of variables and memory management before attempting to fix this code. It should give you a much better idea of what you need to do to make your code do what you want it to do.
Best of luck.
I'm new to C++ programming and in my OPP class we were requested to create a phone book.
Now, in the lecture the Professor said something about that if you want to make sure that your variable that is being injected to a method doesn't get changed you must put const on it.
here is my code so far.
private:
static int phoneCount;
char* name;
char* family;
int phone;
Phone* nextPhone;
public:
int compare(const Phone&other) const;
const char* getFamily();
const char* getName();
and in Phone.cpp
int Phone::compare(const Phone & other) const
{
int result = 0;
result = strcmp(this->family, other.getFamily());
if (result == 0) {
result = strcmp(this->name, other.getName);
}
return 0;
}
I keep getting "the object has type qualifiers that are not compatible with the member"
when I try to call to strcmp inside my compare function.
I know that I can just remove the const in the function declaration and it will go away, but I still doesn't understand why it's showing in the first place.
Help would be greatly appreciated.
You need to add const qualifier for getters const char* getFamily() const;. This way these getters can be invoked on objects of type const Phone & that you pass into function.
Also other.getName should be other.getName().
Your signature
int Phone::compare(const Phone & other) const
means inside that function you need to ensure you don't change the Phone instance.
At the moment, your function calls const char* getFamily() (and getName, which you've missed the () call from). Neither of these functions are const, hence the error.
If you mark these as const too, it will be ok.
In addition to the other answers that correctly suggest const qualifying your getters, you can access the data members of other directly, avoiding those calls.
int Phone::compare(const Phone & other) const
{
int result = strcmp(family, other.family);
if (result == 0) {
result = strcmp(name, other.name);
}
return result;
}
The initial problem is that I have some data to save and retrieve from plate and would like to use a helper struct in the following way:
// this is how I want to use my struct for saving
void safeData()
{
Data data;
data.name = getNameToSave(); // returns const char*
save(data);
}
// ... and in the same way for writing.
void readData()
{
Data data;
read(data);
use(data);
}
... whereas
struct Data
{
const char* name;
// potentially many more data members
}
I'm bound to (const) char* instead of std::string for now because this should be used with legacy code. Unfortunately this will bring up a constness problem:
void read(Data& data)
{
// initial legacy free
free(data.name); // warning: can't convert from const char* to void*
// fill with some data
data.name = getNameFromPlate();
}
So the thing is this: Since my data retrieval methods for reading like getNameFromSomewhere returns const char* I'm bound to making my struct members const, too. This however clashes with write-usage because in this case they must be non-const. What is the best and cleanest way to work around this?
The cleanest way is for the Data struct to own the name data. So when you assign to it you should allocate memory and do a memcpy. This is the only way to make sure that a) you don't leak memory b) the data is not freed before you are done with it and c) data is not changed by other processes in the mean time.
Since you do a copy anyway, you can probably make it more manageable by using std::string instead of const char*. When you assign for const char*, std::string will do the copy for you and you can get a const char* back with c_str(), so that you can still work with the legacy architecture. You don't have to do the switch but will make it easier to manage in the long run.
You have not only a problem of constness, but also a problem of ownership. Except if the API states that the return value of getNameToSave() has to be freed by caller after use, you should not free it. While when you read it from a file, you must free it.
So the simpler way is to always use a local copy that you consistently free after use. As you are required to use old const char * instead of std::string you'd better keep using the good old C library function. Here the best function is strdup that automagically allocate memory and copy old string into it.
An alternative way would be to store the status of the member Data::name in a boolean member :
struct Data
{
const char* name;
bool dyn; // must be freed if true
// potentially many more data members
}
You can then safely use:
if (data.dyn && (data.name != nullptr)) {
free(const_cast<char *>(data.name);
}