I'm top-posting here to give an intro here before I reply to comments in series below.

C++ is a programming language used in the real world by real programmers getting work done. Sometimes we find ourselves in some difficult situations because of a previous bug, or because of a deliberate improvisation to avoid an ABI break.

I'll give you an example from code I wrote just yesterday. I program the microcontrollers inside microscopes for a multinational firm (microcontrollers made by Texas Instruments (CHAR_BIT==16) and Arduino).

A microscope that was designed about 20 years ago was based on a communication system called 'ABC', and a new microscope designed three years ago is based on the 'XYZ' communication system.

There is an extensive SDK for the desktop PC software to interact with the microscopes. For every class beginning with IABC, there is another beginning with IXYZ.

In order to save a lot of time and effort, the class for the new microscope was made to inherit from the old microscope:

    class NewMicroscope : Old Microscope {};

This has been working fine for 3 years but yesterday I spent a few hours trying to figure out why the new microscope can't manually manipulate the COM port. After a few hours I realised that 'NewMicroscope' inherits from a class that inherits from a class that inherits from a class that inherits from 'IABCComHandler'. The problem here is that it should have instead inherited from 'IXYZComHandler'. So the following always yielded a nullptr:

        dynamic_cast<IXYZComHander*>(&new_microscope)

But if I were to change the class hierarchy to make this dynamic_cast possible, then NewMicroscope would no longer inherit from OldMicroscope, but more importantly it would be an ABI break, and I couldn't send out a new DLL file to every customer in a dozen countries just because one customer wants to manually manipulate the COM port (it's a rare request).

So what did I do? In Visual Studio I wrote:

    NewMicroscope obj;
    constexpr void *p1 = &obj;
    constexpr void *p2 = static_cast<IABCComHandler*>(&obj);

Then I just hovered my mouse over the third line and it came up with a tooltip that said '&obj + 16'. Then I found another function in the API that gave back a pointer to another base class whose offset was '&obj + 8'. So I knew that if I could get a pointer to the other class, then I just had to add 8 to it to get the COM port handler. So I sent the customer code that looks like:

    NewMicroscope obj;

    IXYZInterface &inter = obj.GetInterface();

    IXYZComHandler &com = *static_cast<IXYZComHandler*>(static_cast<void*>(static_cast<char*>(static_cast<void*>(&inter)) + 8u));

The code was tested and working before I sent it to them. Is it Ideal to be sending code like this out to customers? No it's not. But I live in the real world.

If C++ is a real world language then it should have a few features in it that allow 'repair jobs' like this. Sure I could ask my compiler vendor to make a change, but isn't the C++ Standard all about making these feature ubiquitous?
    
I reply in series to people below.


On Friday, August 25, 2023, Sebastian Wittmeier wrote:

 - What about called functions? Are the rules also valid within those? What about template functions? What about used operators?



When a function is marked as '__verbose', the verbosity is not extended into nested function calls.


 - All objects are treated as volatile. Including parameters, return values, parameters of function calls (if DoSomething would have parameters)? Including member variables of used classes/structs?



Yes, everything is treated as volatile. Every time the compiler sees the name of a variable inside a function, it has to read its value from memory again -- no assumptions are made, i.e. no optimisation.


 - All pointers are automatically std::launder'ed. When? After each line, after each instruction, after each sub-expression? With launder, you mean p = std::launder(p). Of which pointers is this valid? Any pointer used in the C++ program (are they registered somewhere)? Pointers used in the instructions within the function? What about called functions, operators, ..., which internally use pointers?


Inside the function, every pointer has 'launder' applied to it before every dereference, and there is no caching of the vtable. Every single pointer.

 

 - All objects are treated as volatile. Does that do, what you expect? Variables can be put into processor registers. With the as-if-rule, the optimizer can remove if conditions even for volatile variables. In this case the pointers are provided by the caller, but perhaps this function is inlined and the target of the pointers is known and local to the calling functions.



__verbose functions are forbidden to be inlined.

 

 The overall question is, why would you want to have such a feature?

For making wrong code valid?

For debugging purposes?



I gave one example at the top of this email.

 

Most of the effects you expect would be visible only at the assembler level or in multithreaded code.

For multithreaded code one should use correct synchronization to begin with.

Debugging assembler level is kind of outside of the scope of the C++ standard.

Better each implementation provides a way to generate debug-friendly code, e.g. with a switch like '-O0'.



I don't think that debugging should be beyond the Standard. We have 'assert' and 'NDEBUG' already. More so though here, I'm talking about improvisation rather than debugging.