2

So I have a following snippet (and a good reason behind it):

#include <iostream>

volatile const float A = 10;

int main() {
    volatile const float* ptr = &A;
    float* safePtr = const_cast<float*>(ptr);
    *safePtr = 20;

    std::cout << A << std::endl;

    return 0;
}

Under G++ v8.2.0 (from MinGW suite), this program compiles fine and outputs 20 as expected. Under VS2019, it compiles, but throws a runtime exception - Exception thrown at 0x00007FF7AB961478 in Sandbox.exe: 0xC0000005: Access violation writing location 0x00007FF7AB969C58.

Is there a way to make VS2019 behave the same way the G++ does? And how to do it with CMake?

anastaciu
  • 23,467
  • 7
  • 28
  • 53
doomista
  • 501
  • 2
  • 10
  • "and a good reason behind it" That's an XY problem. What problem are you trying to solve with this code? – n. m. could be an AI Sep 11 '20 at 12:28
  • @n.'pronouns'm. I am coding a game and I have there many constants that require fine tuning over time. I want to have compiler checks for const correctness in my regular code but I also want to have an option to edit those constants at runtime when in debug mode. – doomista Sep 11 '20 at 13:02
  • So such a pseudo-constant is an object that presents two interfaces, an observer interface (for the majority of the code) and an editing interface (for your debug portion of the code). In less fancy words, a getter and a setter. – n. m. could be an AI Sep 11 '20 at 16:58
  • Which would break the API, possibly prevent compiler optimizations in release build and overcomplicate code for definitions of simple numerical constants. I spent better part of today in trying to figure out a way which would not cause major changes to both release and debug versions of the code, but I starting to doubt that C++ has an option for it. – doomista Sep 11 '20 at 19:56
  • So far the closest option to what I need that I found are the `inline` variables from C++17. They don't provide const correctness checks, but other than that can be integrated into existing codebase with the most ease. – doomista Sep 11 '20 at 20:06
  • "Which would break the API". Perhaps an API that relies on something non-constant being a constant is not such a great idea. But if you want to keep it, you can have `float Aimpl = 10; const float& A = Aimpl;` and only export A and not Aimpl. – n. m. could be an AI Sep 11 '20 at 21:16
  • True. I also toyed with the idea of const reference to non-const memory. Unfortunately, all solutions have their downsides and one has to choose whatever is most appropriate in a given situation. – doomista Sep 12 '20 at 07:15

3 Answers3

4

All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.


As governed by [dcl.type.cv]/4, your program has undefined behaviour

Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior. [ Example:

// ...

const int* ciq = new const int (3);  // initialized as required
int* iq = const_cast<int*>(ciq);     // cast required
*iq = 4;                             // undefined: modifies a const object

and as such, demons may fly out of your nose and any kind of analysis of your program beyond this point, including comparison of the behaviour for different compilers, will be a fruitless exercise.

dfrib
  • 70,367
  • 12
  • 127
  • 192
1

You question is interesting, it would seem that const_cast would allow to change an underlying const object, that would be nice indeed, but unfortunately no, const objects cannot be safely changed by any means, even though it appears to be working.

const_cast makes it possible to form a reference or pointer to non-const type that is actually referring to a const object or a reference or pointer to non-volatile type that is actually referring to a volatile object. Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.

You should not try to make this work by ignoring the problem, my guess is that you are running the program in VS in debug mode so it catches the error where g++ doesn't, but if you run your program through the debugger you'll likely see the same problem, though it's not guaranteed as per the nature of undefined behavior.

The way to go is to fix the code, not to ignore the problem.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • _"[...] but if you run your program through the debugger you'll see the same problem."_ - there is no such guarantee. As the OPs program has UB, _everything_ is off the table, and _any_ kind of analysis of such a program may only risk to reinforce the misconception that undefined behaviour can actually be reasoned with (as it may appear to do what one may expect of the same program without UB). – dfrib Sep 11 '20 at 11:22
  • @dfri, yes, I'll refrase. – anastaciu Sep 11 '20 at 11:36
  • _VS2019 is correct._... well technically they're both correct as with UB anything can happen. Just trying to be a little clearer here. – Mike Vine Sep 11 '20 at 11:42
  • 1
    @MikeVine, I believe you are correct, I want to stress that the problem must not be ignored, I refrased it. – anastaciu Sep 11 '20 at 11:46
  • Your guess with debug mode is correct. I would be interested in how to fix my code. I am coding a game and I would like to have some constants baked in, with all appropriate compiler checks, but I also want to have an option to modify them at runtime when in debug mode (so I can have faster development iterations). It is possible to satisfy both of these requirements at once? – doomista Sep 11 '20 at 12:54
  • 1
    @doomista, `const` qualified values can't be changed at runtime in a safe way in any setting, some compilers store them in read-only sections of memory and you're program crashes, some don't and your program appears to be running correctly, this is something that the standard does not allow for, there is no way, I'm aware of, to do somenthing like you request. There are numerous threads about the matter on the site, maybe among them someone has a solution for your problem. – anastaciu Sep 11 '20 at 13:24
0

As point out, you cannot legally modify const objects...

But you can have const reference on non-const object:

so you might use the following:

const float& A = *([]() { static float a = 10; return &a; }());
// const float& A = []() -> float& { static float a = 10; return a; }();


int main() {
    float* safePtr = const_cast<float*>(&A);
    *safePtr = 20;

    std::cout << A << std::endl;
}

(No need of volatile neither).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • This is quite a cool idea, however, cannot be used as a stand it for global const placed in a shared header file. Similar solution would be something like this: `extern int A_ORIG; volatile const int * const A = &A_ORIG;` but that already breaks the existing API as it turns the const into a pointer. :( – doomista Sep 11 '20 at 19:53
  • If you are afraid of ODR violation, you can use template class instead of lambda. – Jarod42 Sep 12 '20 at 16:45